/*
DroidFish - An Android chess program.
Copyright (C) 2011-2013 Peter Österlund, peterosterlund2@gmail.com
Copyright (C) 2012 Leo Mayer
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.if3games.chessonline;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.games.Games;
import com.google.android.gms.games.GamesActivityResultCodes;
import com.google.android.gms.games.GamesStatusCodes;
import com.google.android.gms.games.multiplayer.Invitation;
import com.google.android.gms.games.multiplayer.Multiplayer;
import com.google.android.gms.games.multiplayer.OnInvitationReceivedListener;
import com.google.android.gms.games.multiplayer.Participant;
import com.google.android.gms.games.multiplayer.realtime.RealTimeMessage;
import com.google.android.gms.games.multiplayer.realtime.RealTimeMessageReceivedListener;
import com.google.android.gms.games.multiplayer.realtime.RealTimeMultiplayer.ReliableMessageSentCallback;
import com.google.android.gms.games.multiplayer.realtime.Room;
import com.google.android.gms.games.multiplayer.realtime.RoomConfig;
import com.google.android.gms.games.multiplayer.realtime.RoomStatusUpdateListener;
import com.google.android.gms.games.multiplayer.realtime.RoomUpdateListener;
import com.google.android.gms.plus.Plus;
import com.google.android.gms.appstate.AppStateManager;
import com.google.android.gms.appstate.AppStateStatusCodes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.example.games.basegameutils.BaseGameActivity;
import com.if3games.chessonline.ChessBoard.SquareDecoration;
import com.if3games.chessonline.activities.CPUWarning;
import com.if3games.chessonline.activities.EditBoard;
import com.if3games.chessonline.activities.EditPGNLoad;
import com.if3games.chessonline.activities.EditPGNSave;
import com.if3games.chessonline.activities.LoadFEN;
import com.if3games.chessonline.activities.LoadScid;
import com.if3games.chessonline.activities.Preferences;
import com.if3games.chessonline.book.BookOptions;
import com.if3games.chessonline.data.ConstantsData;
import com.if3games.chessonline.data.SaveGame;
import com.if3games.chessonline.engine.EngineUtil;
import com.if3games.chessonline.gamelogic.ChessParseError;
import com.if3games.chessonline.gamelogic.DroidChessController;
import com.if3games.chessonline.gamelogic.Move;
import com.if3games.chessonline.gamelogic.Pair;
import com.if3games.chessonline.gamelogic.PgnToken;
import com.if3games.chessonline.gamelogic.Piece;
import com.if3games.chessonline.gamelogic.Position;
import com.if3games.chessonline.gamelogic.TextIO;
import com.if3games.chessonline.gamelogic.TimeControlData;
import com.if3games.chessonline.gamelogic.GameTree.Node;
import com.if3games.chessonline.gtb.Probe;
import com.if3games.chessonline.other.PauseTimer;
import com.larvalabs.svgandroid.SVG;
import com.larvalabs.svgandroid.SVGParser;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.database.SQLException;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.StateListDrawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.text.ClipboardManager;
import android.text.Html;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.BackgroundColorSpan;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.StyleSpan;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
public class DroidFish extends BaseGameActivity implements GUIInterface, OnClickListener, RealTimeMessageReceivedListener,
RoomStatusUpdateListener, RoomUpdateListener, OnInvitationReceivedListener, ReliableMessageSentCallback {
// FIXME!!! book.txt (and test classes) should not be included in apk
// FIXME!!! PGN view option: game continuation (for training)
// FIXME!!! Remove invalid playerActions in PGN import (should be done in verifyChildren)
// FIXME!!! Implement bookmark mechanism for positions in pgn files
// FIXME!!! Add support for "Chess Leipzig" font
// FIXME!!! Computer clock should stop if phone turned off (computer stops thinking if unplugged)
// FIXME!!! Add support for "no time control" and "hour-glass time control" as defined by the PGN standard
// FIXME!!! Online play on FICS
// FIXME!!! Add chess960 support
// FIXME!!! Implement "hint" feature
// FIXME!!! Show extended book info. (Win percent, number of games, performance rating, etc.)
// FIXME!!! Green color for "main move". Red color for "don't play in tournaments" moves.
// FIXME!!! ECO opening codes
// FIXME!!! Remember multi-PV analysis setting when program restarted.
// FIXME!!! Option to display coordinates in border outside chess board.
// FIXME!!! Better behavior if engine is terminated. How exactly?
// FIXME!!! Handle PGN non-file intents with more than one game.
// FIXME!!! Save position to fen/epd file
// FIXME!!! Strength setting for external engines
// FIXME!!! Selection dialog for going into variation
// FIXME!!! Use two engines in engine/engine games
private ChessBoardPlay cb;
private static DroidChessController ctrl = null;
private boolean mShowThinking;
private boolean mShowStats;
private boolean mWhiteBasedScores;
private boolean mShowBookHints;
private int maxNumArrows;
private GameMode gameMode;
private boolean mPonderMode;
private int timeControl;
private int movesPerSession;
private int timeIncrement;
private int mEngineThreads;
private String playerName;
private boolean boardFlipped;
private boolean autoSwapSides;
private boolean playerNameFlip;
private boolean discardVariations;
private TextView status;
private ScrollView moveListScroll;
private TextView moveList;
private TextView thinking;
private ImageButton custom1Button, custom2Button, custom3Button;
private ImageButton modeButton, undoButton, redoButton;
private ButtonActions custom1ButtonActions, custom2ButtonActions, custom3ButtonActions;
private TextView whiteTitleText, blackTitleText, engineTitleText;
private View secondTitleLine;
private TextView whiteFigText, blackFigText, summaryTitleText;
private static Dialog moveListMenuDlg;
SharedPreferences settings;
private boolean boardGestures;
private float scrollSensitivity;
private boolean invertScrollDirection;
private boolean leftHanded;
private boolean soundEnabled;
private MediaPlayer moveSound;
private boolean vibrateEnabled;
private boolean animateMoves;
private boolean autoScrollTitle;
private boolean showMaterialDiff;
private boolean showVariationLine;
private final static String bookDir = "DroidFish";
private final static String pgnDir = "DroidFish/pgn";
private final static String fenDir = "DroidFish/epd";
private final static String engineDir = "DroidFish/uci";
private final static String gtbDefaultDir = "DroidFish/gtb";
private BookOptions bookOptions = new BookOptions();
private PGNOptions pgnOptions = new PGNOptions();
private EngineOptions engineOptions = new EngineOptions();
private long lastVisibleMillis; // Time when GUI became invisible. 0 if currently visible.
private long lastComputationMillis; // Time when engine last showed that it was computing.
PgnScreenText gameTextListener;
private WakeLock wakeLock = null;
private boolean useWakeLock = false;
private Typeface figNotation;
private Typeface defaultMoveListTypeFace;
private Typeface defaultThinkingListTypeFace;
/** Defines all configurable button actions. */
private ActionFactory actionFactory = new ActionFactory() {
private HashMap<String, UIAction> actions;
private void addAction(UIAction a) {
actions.put(a.getId(), a);
}
{
actions = new HashMap<String, UIAction>();
addAction(new UIAction() {
public String getId() { return "flipboard"; }
public int getName() { return R.string.flip_board; }
public int getIcon() { return R.raw.flip; }
public boolean enabled() { return true; }
public void run() {
boardFlipped = !cb.flipped;
setBooleanPref("boardFlipped", boardFlipped);
cb.setFlipped(boardFlipped);
}
});
addAction(new UIAction() {
public String getId() { return "showThinking"; }
public int getName() { return R.string.toggle_show_thinking; }
public int getIcon() { return R.raw.thinking; }
public boolean enabled() { return true; }
public void run() {
mShowThinking = toggleBooleanPref("showThinking");
updateThinkingInfo();
}
});
addAction(new UIAction() {
public String getId() { return "bookHints"; }
public int getName() { return R.string.toggle_book_hints; }
public int getIcon() { return R.raw.book; }
public boolean enabled() { return true; }
public void run() {
mShowBookHints = toggleBooleanPref("bookHints");
updateThinkingInfo();
}
});
addAction(new UIAction() {
public String getId() { return "viewVariations"; }
public int getName() { return R.string.toggle_pgn_variations; }
public int getIcon() { return R.raw.variation; }
public boolean enabled() { return true; }
public void run() {
pgnOptions.view.variations = toggleBooleanPref("viewVariations");
gameTextListener.clear();
ctrl.prefsChanged(false);
}
});
addAction(new UIAction() {
public String getId() { return "viewComments"; }
public int getName() { return R.string.toggle_pgn_comments; }
public int getIcon() { return R.raw.comment; }
public boolean enabled() { return true; }
public void run() {
pgnOptions.view.comments = toggleBooleanPref("viewComments");
gameTextListener.clear();
ctrl.prefsChanged(false);
}
});
addAction(new UIAction() {
public String getId() { return "viewHeaders"; }
public int getName() { return R.string.toggle_pgn_headers; }
public int getIcon() { return R.raw.header; }
public boolean enabled() { return true; }
public void run() {
pgnOptions.view.headers = toggleBooleanPref("viewHeaders");
gameTextListener.clear();
ctrl.prefsChanged(false);
}
});
addAction(new UIAction() {
public String getId() { return "toggleAnalysis"; }
public int getName() { return R.string.toggle_analysis; }
public int getIcon() { return R.raw.analyze; }
public boolean enabled() { return true; }
private int oldGameModeType = GameMode.EDIT_GAME;
public void run() {
int gameModeType;
if (ctrl.analysisMode()) {
gameModeType = oldGameModeType;
} else {
oldGameModeType = ctrl.getGameMode().getModeNr();
gameModeType = GameMode.ANALYSIS;
}
newGameMode(gameModeType);
setBoardFlip(true);
}
});
addAction(new UIAction() {
public String getId() { return "largeButtons"; }
public int getName() { return R.string.toggle_large_buttons; }
public int getIcon() { return R.raw.magnify; }
public boolean enabled() { return true; }
public void run() {
pgnOptions.view.headers = toggleBooleanPref("largeButtons");
updateButtons();
}
});
addAction(new UIAction() {
public String getId() { return "blindMode"; }
public int getName() { return R.string.blind_mode; }
public int getIcon() { return R.raw.blind; }
public boolean enabled() { return true; }
public void run() {
boolean blindMode = !cb.blindMode;
setBooleanPref("blindMode", blindMode);
cb.setBlindMode(blindMode);
}
});
addAction(new UIAction() {
public String getId() { return "loadLastFile"; }
public int getName() { return R.string.load_last_file; }
public int getIcon() { return R.raw.open_last_file; }
public boolean enabled() { return currFileType() != FT_NONE; }
public void run() {
loadLastFile();
}
});
addAction(new UIAction() {
public String getId() { return "selectEngine"; }
public int getName() { return R.string.select_engine; }
public int getIcon() { return R.raw.engine; }
public boolean enabled() { return true; }
public void run() {
removeDialog(SELECT_ENGINE_DIALOG_NOMANAGE);
showDialog(SELECT_ENGINE_DIALOG_NOMANAGE);
}
});
}
@Override
public UIAction getAction(String actionId) {
return actions.get(actionId);
}
};
// GMS Multiplayer
private boolean isSinglePlayer;
private boolean unlockLetterGridBtn = false;
final static String TAG = "MULTIPLAER";
// request codes we use when invoking an external activity
final int RC_RESOLVE = 5000, RC_UNUSED = 5001;
// Request codes for the UIs that we show with startActivityForResult:
final static int RC_SELECT_PLAYERS = 10000;
final static int RC_INVITATION_INBOX = 10001;
final static int RC_WAITING_ROOM = 10002;
// Room ID where the currently active game is taking place; null if we're
// not playing.
String mRoomId = null;
// Are we playing in multiplayer mode?
boolean mMultiplayer = false;
// The participants in the currently active game
ArrayList<Participant> mParticipants = null;
// My participant ID in the currently active game
String mMyId = null;
// If non-null, this is the id of the invitation we received via the
// invitation listener
String mIncomingInvitationId = null;
//private ExpiredTimeDialog nLevelDialog;
private boolean imReady = false;
private boolean opponentReady = false;
private boolean isOpponentResign = false;
private boolean isOpponentTimeOut = false;
private boolean isLeaveRoom = false;
private Handler mHandler;
private Runnable myRunnable;
private boolean isMatch = false;
// For Cloud Save
private static final int OUR_STATE_KEY = 0;
// whether we already loaded the state the first time (so we don't reload
// every time the activity goes to the background and comes back to the foreground)
boolean mAlreadyLoadedState = false;
boolean mAlreadyLocalState = false;
// current save game
private SaveGame mSaveGame = new SaveGame();
private GoogleApiClient mClient;
private Map<String,Integer> mOpponentStats = new HashMap<String,Integer>(ConstantsData.initMap);
private String opponentName;
private int opponentRating;
private int gameTypeMode = 0;
private int gmsGameVariantNumber = -1;
private TextView player1TitleText, player2TitleText;
private int imFirstType = -1;
private boolean myTurn = false;
private boolean invalidMove = false;
private boolean opponentLeave = true;
private InterstitialAd interstitial;
private AdRequest adRequest;
private AdView adView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int isGMS = getIntent().getExtras().getInt("gms");
if (isGMS != 1) {
isSinglePlayer = true;
} else {
isSinglePlayer = false;
GoogleApiClient.Builder builder =
new GoogleApiClient.Builder(this);
builder.addApi(Games.API)
.addApi(Plus.API)
.addApi(AppStateManager.API)
.addScope(Games.SCOPE_GAMES)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.addScope(AppStateManager.SCOPE_APP_STATE);
mClient = builder.build();
loadLocal();
if(isSignedIn()) {
//onFetchPlayerScoreAndAchive();
//displayPlayerNameScoreRank();
//Toast.makeText(this, "I Connected", Toast.LENGTH_SHORT).show();
}
}
Pair<String,String> pair = getPgnOrFenIntent();
String intentPgnOrFen = pair.first;
String intentFilename = pair.second;
createDirectories();
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
settings = PreferenceManager.getDefaultSharedPreferences(this);
settings.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
try {
handlePrefsChange();
} catch (Exception e) {
// TODO: handle exception
}
}
});
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
setWakeLock(false);
wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "droidfish");
wakeLock.setReferenceCounted(false);
custom1ButtonActions = new ButtonActions("custom1", CUSTOM1_BUTTON_DIALOG,
R.string.select_action);
custom2ButtonActions = new ButtonActions("custom2", CUSTOM2_BUTTON_DIALOG,
R.string.select_action);
custom3ButtonActions = new ButtonActions("custom3", CUSTOM3_BUTTON_DIALOG,
R.string.select_action);
figNotation = Typeface.createFromAsset(getAssets(), "fonts/DroidFishChessNotationDark.otf");
setPieceNames(PGNOptions.PT_LOCAL);
//requestWindowFeature(Window.FEATURE_NO_TITLE);
initUI();
gameTextListener = new PgnScreenText(pgnOptions);
if (ctrl != null)
ctrl.shutdownEngine();
ctrl = new DroidChessController(this, gameTextListener, pgnOptions);
egtbForceReload = true;
readPrefs();
TimeControlData tcData = new TimeControlData();
tcData.setTimeControl(timeControl, movesPerSession, timeIncrement);
if(isSinglePlayer)
{
myTurn = true;
ctrl.newGame(gameMode, tcData);
{
byte[] data = null;
int version = 1;
if (savedInstanceState != null) {
data = savedInstanceState.getByteArray("gameState");
version = savedInstanceState.getInt("gameStateVersion", version);
} else {
String dataStr = settings.getString("gameState", null);
version = settings.getInt("gameStateVersion", version);
if (dataStr != null)
data = strToByteArr(dataStr);
}
if (data != null)
ctrl.fromByteArray(data, version);
}
ctrl.setGuiPaused(true);
ctrl.setGuiPaused(false);
ctrl.startGame();
//startNewGame(0);
if (intentPgnOrFen != null) {
try {
ctrl.setFENOrPGN(intentPgnOrFen);
setBoardFlip(true);
} catch (ChessParseError e) {
// If FEN corresponds to illegal chess position, go into edit board mode.
try {
TextIO.readFEN(intentPgnOrFen);
} catch (ChessParseError e2) {
if (e2.pos != null)
startEditBoard(intentPgnOrFen);
}
}
} else if (intentFilename != null) {
if (intentFilename.toLowerCase(Locale.US).endsWith(".fen") ||
intentFilename.toLowerCase(Locale.US).endsWith(".epd"))
loadFENFromFile(intentFilename);
else
loadPGNFromFile(intentFilename);
}
}
else
{
int rnd = new Random().nextInt(2);
startMultiplayerGameMode(rnd);
}
}
// Unicode code points for chess pieces
private static final String figurinePieceNames = Piece.NOTATION_PAWN + " " +
Piece.NOTATION_KNIGHT + " " +
Piece.NOTATION_BISHOP + " " +
Piece.NOTATION_ROOK + " " +
Piece.NOTATION_QUEEN + " " +
Piece.NOTATION_KING;
private final void setPieceNames(int pieceType) {
if (pieceType == PGNOptions.PT_FIGURINE) {
TextIO.setPieceNames(figurinePieceNames);
} else {
TextIO.setPieceNames(getString(R.string.piece_names));
}
}
/** Create directory structure on SD card. */
private final void createDirectories() {
File extDir = Environment.getExternalStorageDirectory();
String sep = File.separator;
new File(extDir + sep + bookDir).mkdirs();
new File(extDir + sep + pgnDir).mkdirs();
new File(extDir + sep + fenDir).mkdirs();
new File(extDir + sep + engineDir).mkdirs();
new File(extDir + sep + gtbDefaultDir).mkdirs();
}
/**
* Return PGN/FEN data or filename from the Intent. Both can not be non-null.
* @return Pair of PGN/FEN data and filename.
*/
private final Pair<String,String> getPgnOrFenIntent() {
String pgnOrFen = null;
String filename = null;
try {
Intent intent = getIntent();
Uri data = intent.getData();
if (data == null) {
Bundle b = intent.getExtras();
if (b != null) {
Object strm = b.get(Intent.EXTRA_STREAM);
if (strm instanceof Uri) {
data = (Uri)strm;
if ("file".equals(data.getScheme())) {
filename = data.getEncodedPath();
if (filename != null)
filename = Uri.decode(filename);
}
}
}
}
if (data == null) {
if ((Intent.ACTION_SEND.equals(intent.getAction()) ||
Intent.ACTION_VIEW.equals(intent.getAction())) &&
("application/x-chess-pgn".equals(intent.getType()) ||
"application/x-chess-fen".equals(intent.getType())))
pgnOrFen = intent.getStringExtra(Intent.EXTRA_TEXT);
} else {
String scheme = intent.getScheme();
if ("file".equals(scheme)) {
filename = data.getEncodedPath();
if (filename != null)
filename = Uri.decode(filename);
}
if ((filename == null) &&
("content".equals(scheme) ||
"file".equals(scheme))) {
ContentResolver resolver = getContentResolver();
InputStream in = resolver.openInputStream(intent.getData());
StringBuilder sb = new StringBuilder();
while (true) {
byte[] buffer = new byte[16384];
int len = in.read(buffer);
if (len <= 0)
break;
sb.append(new String(buffer, 0, len));
}
pgnOrFen = sb.toString();
}
}
} catch (IOException e) {
Toast.makeText(getApplicationContext(), R.string.failed_to_read_pgn_data,
Toast.LENGTH_SHORT).show();
}
return new Pair<String,String>(pgnOrFen,filename);
}
private final byte[] strToByteArr(String str) {
if (str == null)
return null;
int nBytes = str.length() / 2;
byte[] ret = new byte[nBytes];
for (int i = 0; i < nBytes; i++) {
int c1 = str.charAt(i * 2) - 'A';
int c2 = str.charAt(i * 2 + 1) - 'A';
ret[i] = (byte)(c1 * 16 + c2);
}
return ret;
}
private final String byteArrToString(byte[] data) {
if (data == null)
return null;
StringBuilder ret = new StringBuilder(32768);
int nBytes = data.length;
for (int i = 0; i < nBytes; i++) {
int b = data[i]; if (b < 0) b += 256;
char c1 = (char)('A' + (b / 16));
char c2 = (char)('A' + (b & 15));
ret.append(c1);
ret.append(c2);
}
return ret.toString();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
reInitUI();
}
/** Re-initialize UI when layout should change because of rotation or handedness change. */
private final void reInitUI() {
ChessBoardPlay oldCB = cb;
String statusStr = status.getText().toString();
initUI();
readPrefs();
cb.cursorX = oldCB.cursorX;
cb.cursorY = oldCB.cursorY;
cb.cursorVisible = oldCB.cursorVisible;
cb.setPosition(oldCB.pos);
cb.setFlipped(oldCB.flipped);
cb.setDrawSquareLabels(oldCB.drawSquareLabels);
cb.oneTouchMoves = oldCB.oneTouchMoves;
cb.toggleSelection = oldCB.toggleSelection;
cb.highlightLastMove = oldCB.highlightLastMove;
cb.setBlindMode(oldCB.blindMode);
setSelection(oldCB.selectedSquare);
cb.userSelectedSquare = oldCB.userSelectedSquare;
setStatusString(statusStr);
moveListUpdated();
updateThinkingInfo();
ctrl.updateRemainingTime();
ctrl.updateMaterialDiffList();
}
/** Return true if left-handed layout should be used. */
private final boolean leftHandedView() {
return settings.getBoolean("leftHanded", false) &&
(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}
/** Re-read preferences settings. */
private final void handlePrefsChange() {
if (leftHanded != leftHandedView())
reInitUI();
else
readPrefs();
ctrl.setGameMode(gameMode);
}
private final void initUI() {
leftHanded = leftHandedView();
if(!isSinglePlayer) {
setContentView(leftHanded ? R.layout.main_left_handed_gms : R.layout.main_gms);
for (int id : CLICKABLES) {
findViewById(id).setOnClickListener(this);
}
}
else {
setContentView(leftHanded ? R.layout.main_left_handed : R.layout.main);
}
Util.overrideFonts(findViewById(android.R.id.content));
// title lines need to be regenerated every time due to layout changes (rotations)
secondTitleLine = findViewById(R.id.second_title_line);
whiteTitleText = (TextView)findViewById(R.id.white_clock);
whiteTitleText.setSelected(true);
blackTitleText = (TextView)findViewById(R.id.black_clock);
blackTitleText.setSelected(true);
engineTitleText = (TextView)findViewById(R.id.title_text);
whiteFigText = (TextView)findViewById(R.id.white_pieces);
whiteFigText.setTypeface(figNotation);
whiteFigText.setSelected(true);
whiteFigText.setTextColor(whiteTitleText.getTextColors());
blackFigText = (TextView)findViewById(R.id.black_pieces);
blackFigText.setTypeface(figNotation);
blackFigText.setSelected(true);
blackFigText.setTextColor(blackTitleText.getTextColors());
summaryTitleText = (TextView)findViewById(R.id.title_text_summary);
player1TitleText = (TextView)findViewById(R.id.player1);
player2TitleText = (TextView)findViewById(R.id.player2);
status = (TextView)findViewById(R.id.status);
moveListScroll = (ScrollView)findViewById(R.id.scrollView);
moveList = (TextView)findViewById(R.id.moveList);
defaultMoveListTypeFace = moveList.getTypeface();
thinking = (TextView)findViewById(R.id.thinking);
defaultThinkingListTypeFace = thinking.getTypeface();
status.setFocusable(false);
moveListScroll.setFocusable(false);
moveList.setFocusable(false);
moveList.setMovementMethod(LinkMovementMethod.getInstance());
thinking.setFocusable(false);
cb = (ChessBoardPlay)findViewById(R.id.chessboard);
cb.setFocusable(true);
cb.requestFocus();
cb.setClickable(true);
cb.setPgnOptions(pgnOptions);
final GestureDetector gd = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
private float scrollX = 0;
private float scrollY = 0;
@Override
public boolean onDown(MotionEvent e) {
if (!boardGestures) {
handleClick(e);
return true;
}
scrollX = 0;
scrollY = 0;
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!boardGestures)
return false;
cb.cancelLongPress();
if (invertScrollDirection) {
distanceX = -distanceX;
distanceY = -distanceY;
}
if ((scrollSensitivity > 0) && (cb.sqSize > 0)) {
scrollX += distanceX;
scrollY += distanceY;
float scrollUnit = cb.sqSize * scrollSensitivity;
if (Math.abs(scrollX) >= Math.abs(scrollY)) {
// Undo/redo
int nRedo = 0, nUndo = 0;
while (scrollX > scrollUnit) {
nRedo++;
scrollX -= scrollUnit;
}
while (scrollX < -scrollUnit) {
nUndo++;
scrollX += scrollUnit;
}
if (nUndo + nRedo > 0)
scrollY = 0;
if (nRedo + nUndo > 1) {
boolean analysis = gameMode.analysisMode();
boolean human = gameMode.playerWhite() || gameMode.playerBlack();
if (analysis || !human)
ctrl.setGameMode(new GameMode(GameMode.TWO_PLAYERS));
}
for (int i = 0; i < nRedo; i++) ctrl.redoMove();
for (int i = 0; i < nUndo; i++) ctrl.undoMove();
ctrl.setGameMode(gameMode);
} else {
// Next/previous variation
int varDelta = 0;
while (scrollY > scrollUnit) {
varDelta++;
scrollY -= scrollUnit;
}
while (scrollY < -scrollUnit) {
varDelta--;
scrollY += scrollUnit;
}
if (varDelta != 0)
scrollX = 0;
ctrl.changeVariation(varDelta);
}
}
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (!boardGestures)
return false;
cb.cancelLongPress();
handleClick(e);
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
if (!boardGestures)
return false;
if (e.getAction() == MotionEvent.ACTION_UP)
handleClick(e);
return true;
}
private final void handleClick(MotionEvent e) {
if (ctrl.humansTurn() && myTurn) {
int sq = cb.eventToSquare(e);
Move m = cb.mousePressed(sq);
if (m != null) {
ctrl.makeHumanMove(m);
if(!isSinglePlayer) {
if(!invalidMove)
broadcastMove(m.to, m.from);
else
invalidMove = false;
}
}
setEgtbHints(cb.getSelectedSquare());
}
}
@Override
public void onLongPress(MotionEvent e) {
if (!boardGestures)
return;
((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(20);
removeDialog(BOARD_MENU_DIALOG);
showDialog(BOARD_MENU_DIALOG);
}
});
cb.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return gd.onTouchEvent(event);
}
});
cb.setOnTrackballListener(new ChessBoard.OnTrackballListener() {
public void onTrackballEvent(MotionEvent event) {
if (ctrl.humansTurn()) {
Move m = cb.handleTrackballEvent(event);
if (m != null)
ctrl.makeHumanMove(m);
setEgtbHints(cb.getSelectedSquare());
}
}
});
moveList.setOnLongClickListener(new OnLongClickListener() {
public boolean onLongClick(View v) {
removeDialog(MOVELIST_MENU_DIALOG);
showDialog(MOVELIST_MENU_DIALOG);
return true;
}
});
thinking.setOnLongClickListener(new OnLongClickListener() {
public boolean onLongClick(View v) {
if (mShowThinking || gameMode.analysisMode()) {
if (!pvMoves.isEmpty()) {
removeDialog(THINKING_MENU_DIALOG);
showDialog(THINKING_MENU_DIALOG);
}
}
return true;
}
});
custom1Button = (ImageButton)findViewById(R.id.custom1Button);
custom1ButtonActions.setImageButton(custom1Button, this);
custom2Button = (ImageButton)findViewById(R.id.custom2Button);
custom2ButtonActions.setImageButton(custom2Button, this);
custom3Button = (ImageButton)findViewById(R.id.custom3Button);
custom3ButtonActions.setImageButton(custom3Button, this);
modeButton = (ImageButton)findViewById(R.id.modeButton);
modeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(isSinglePlayer)
showDialog(GAME_MODE_DIALOG);
else
showDialog(GAME_GMS_MODE_DIALOG);
}
});
undoButton = (ImageButton)findViewById(R.id.undoButton);
redoButton = (ImageButton)findViewById(R.id.redoButton);
if(isSinglePlayer) {
undoButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ctrl.undoMove();
}
});
undoButton.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
removeDialog(GO_BACK_MENU_DIALOG);
showDialog(GO_BACK_MENU_DIALOG);
return true;
}
});
redoButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ctrl.redoMove();
}
});
redoButton.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
removeDialog(GO_FORWARD_MENU_DIALOG);
showDialog(GO_FORWARD_MENU_DIALOG);
return true;
}
});
} else {
undoButton.setVisibility(View.GONE);
redoButton.setVisibility(View.GONE);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(isSinglePlayer) {
if (ctrl != null) {
byte[] data = ctrl.toByteArray();
outState.putByteArray("gameState", data);
outState.putInt("gameStateVersion", 3);
}
}
}
@Override
protected void onResume() {
lastVisibleMillis = 0;
if (ctrl != null)
ctrl.setGuiPaused(false);
notificationActive = true;
updateNotification();
setWakeLock(useWakeLock);
super.onResume();
}
@Override
protected void onPause() {
if (ctrl != null) {
ctrl.setGuiPaused(true);
if(isSinglePlayer) {
byte[] data = ctrl.toByteArray();
Editor editor = settings.edit();
String dataStr = byteArrToString(data);
editor.putString("gameState", dataStr);
editor.putInt("gameStateVersion", 3);
editor.commit();
}
}
lastVisibleMillis = System.currentTimeMillis();
updateNotification();
setWakeLock(false);
super.onPause();
}
@Override
protected void onDestroy() {
if (ctrl != null)
ctrl.shutdownEngine();
setNotification(false);
super.onDestroy();
}
private final int getIntSetting(String settingName, int defaultValue) {
String tmp = settings.getString(settingName, String.format(Locale.US, "%d", defaultValue));
int value = Integer.parseInt(tmp);
return value;
}
private final void readPrefs() {
if (isSinglePlayer) {
int modeNr = getIntSetting("gameMode", 1);
gameMode = new GameMode(modeNr);
String oldPlayerName = playerName;
playerName = settings.getString("playerName", "Player");
boardFlipped = settings.getBoolean("boardFlipped", false);
autoSwapSides = settings.getBoolean("autoSwapSides", false);
playerNameFlip = settings.getBoolean("playerNameFlip", true);
setBoardFlip(!playerName.equals(oldPlayerName));
boolean drawSquareLabels = settings.getBoolean("drawSquareLabels", false);
cb.setDrawSquareLabels(drawSquareLabels);
cb.oneTouchMoves = settings.getBoolean("oneTouchMoves", false);
cb.toggleSelection = getIntSetting("squareSelectType", 0) == 1;
cb.highlightLastMove = settings.getBoolean("highlightLastMove", true);
cb.setBlindMode(settings.getBoolean("blindMode", false));
mShowThinking = settings.getBoolean("showThinking", false);
mShowStats = settings.getBoolean("showStats", true);
mWhiteBasedScores = settings.getBoolean("whiteBasedScores", false);
maxNumArrows = getIntSetting("thinkingArrows", 2);
mShowBookHints = settings.getBoolean("bookHints", false);
mEngineThreads = getIntSetting("threads", 1);
String engine = settings.getString("engine", "stockfish");
int strength = settings.getInt("strength", 1000);
setEngineStrength(engine, strength);
mPonderMode = settings.getBoolean("ponderMode", false);
if (!mPonderMode)
ctrl.stopPonder();
timeControl = getIntSetting("timeControl", 120000);
movesPerSession = getIntSetting("movesPerSession", 60);
timeIncrement = getIntSetting("timeIncrement", 0);
boardGestures = settings.getBoolean("boardGestures", true);
scrollSensitivity = Float.parseFloat(settings.getString("scrollSensitivity", "2"));
invertScrollDirection = settings.getBoolean("invertScrollDirection", false);
discardVariations = settings.getBoolean("discardVariations", false);
Util.setFullScreenMode(this, settings);
useWakeLock = settings.getBoolean("wakeLock", false);
setWakeLock(useWakeLock);
// Visible Options
int fontSize = getIntSetting("fontSize", 12);
int statusFontSize = fontSize;
Configuration config = getResources().getConfiguration();
if (config.orientation == Configuration.ORIENTATION_PORTRAIT)
statusFontSize = Math.min(statusFontSize, 16);
status.setTextSize(statusFontSize);
moveList.setTextSize(fontSize);
thinking.setTextSize(fontSize);
soundEnabled = settings.getBoolean("soundEnabled", false);
vibrateEnabled = settings.getBoolean("vibrateEnabled", false);
animateMoves = settings.getBoolean("animateMoves", true);
autoScrollTitle = settings.getBoolean("autoScrollTitle", true);
setTitleScrolling();
ColorTheme.instance().readColors(settings);
cb.setColors();
Util.overrideFonts(findViewById(android.R.id.content));
// End Visible Options
custom1ButtonActions.readPrefs(settings, actionFactory);
custom2ButtonActions.readPrefs(settings, actionFactory);
custom3ButtonActions.readPrefs(settings, actionFactory);
updateButtons();
bookOptions.filename = settings.getString("bookFile", "");
bookOptions.maxLength = getIntSetting("bookMaxLength", 1000000);
bookOptions.preferMainLines = settings.getBoolean("bookPreferMainLines", false);
bookOptions.tournamentMode = settings.getBoolean("bookTournamentMode", false);
bookOptions.random = (settings.getInt("bookRandom", 500) - 500) * (3.0 / 500);
setBookOptions();
engineOptions.hashMB = getIntSetting("hashMB", 16);
engineOptions.hints = settings.getBoolean("tbHints", false);
engineOptions.hintsEdit = settings.getBoolean("tbHintsEdit", false);
engineOptions.rootProbe = settings.getBoolean("tbRootProbe", true);
engineOptions.engineProbe = settings.getBoolean("tbEngineProbe", true);
String gtbPath = settings.getString("gtbPath", "").trim();
if (gtbPath.length() == 0) {
File extDir = Environment.getExternalStorageDirectory();
String sep = File.separator;
gtbPath = extDir.getAbsolutePath() + sep + gtbDefaultDir;
}
engineOptions.gtbPath = gtbPath;
setEngineOptions(false);
setEgtbHints(cb.getSelectedSquare());
updateThinkingInfo();
pgnOptions.view.variations = settings.getBoolean("viewVariations", true);
pgnOptions.view.comments = settings.getBoolean("viewComments", true);
pgnOptions.view.nag = settings.getBoolean("viewNAG", true);
pgnOptions.view.headers = settings.getBoolean("viewHeaders", false);
final int oldViewPieceType = pgnOptions.view.pieceType;
pgnOptions.view.pieceType = getIntSetting("viewPieceType", PGNOptions.PT_LOCAL);
showVariationLine = settings.getBoolean("showVariationLine", false);
pgnOptions.imp.variations = settings.getBoolean("importVariations", true);
pgnOptions.imp.comments = settings.getBoolean("importComments", true);
pgnOptions.imp.nag = settings.getBoolean("importNAG", true);
pgnOptions.exp.variations = settings.getBoolean("exportVariations", true);
pgnOptions.exp.comments = settings.getBoolean("exportComments", true);
pgnOptions.exp.nag = settings.getBoolean("exportNAG", true);
pgnOptions.exp.playerAction = settings.getBoolean("exportPlayerAction", false);
pgnOptions.exp.clockInfo = settings.getBoolean("exportTime", false);
gameTextListener.clear();
setPieceNames(pgnOptions.view.pieceType);
ctrl.prefsChanged(oldViewPieceType != pgnOptions.view.pieceType);
// update the typeset in case of a change anyway, cause it could occur
// as well in rotation
setFigurineNotation(pgnOptions.view.pieceType == PGNOptions.PT_FIGURINE, fontSize);
showMaterialDiff = settings.getBoolean("materialDiff", false);
secondTitleLine.setVisibility(showMaterialDiff ? View.VISIBLE : View.GONE);
}
else {
int modeNr = 1;
gameMode = new GameMode(modeNr);
String oldPlayerName = playerName;
playerName = "Player";
boardFlipped = false;
autoSwapSides = false;
playerNameFlip = true;
setBoardFlip(!playerName.equals(oldPlayerName));
boolean drawSquareLabels = false;
cb.setDrawSquareLabels(drawSquareLabels);
cb.oneTouchMoves = false;
cb.toggleSelection = getIntSetting("squareSelectType", 0) == 1;
cb.highlightLastMove = settings.getBoolean("highlightLastMove", true);
cb.setBlindMode(false);
mShowThinking = false;
mShowStats = settings.getBoolean("showStats", true);
mWhiteBasedScores = false;
maxNumArrows = getIntSetting("thinkingArrows", 2);
mShowBookHints = false;
mEngineThreads = 1;
String engine = "stockfish";
int strength = 1000;
setEngineStrength(engine, strength);
mPonderMode = false;
if (!mPonderMode)
ctrl.stopPonder();
// Toto: add time control to gms
timeControl = 120000;
movesPerSession = 60;
timeIncrement = 0;
boardGestures = false;
scrollSensitivity = Float.parseFloat(settings.getString("scrollSensitivity", "2"));
invertScrollDirection = settings.getBoolean("invertScrollDirection", false);
discardVariations = false;
Util.setFullScreenMode(this, settings);
useWakeLock = false;
setWakeLock(useWakeLock);
// Visible Options
int fontSize = getIntSetting("fontSize", 12);
int statusFontSize = fontSize;
Configuration config = getResources().getConfiguration();
if (config.orientation == Configuration.ORIENTATION_PORTRAIT)
statusFontSize = Math.min(statusFontSize, 16);
status.setTextSize(statusFontSize);
moveList.setTextSize(fontSize);
thinking.setTextSize(fontSize);
soundEnabled = settings.getBoolean("soundEnabled", false);
vibrateEnabled = settings.getBoolean("vibrateEnabled", false);
animateMoves = settings.getBoolean("animateMoves", true);
autoScrollTitle = settings.getBoolean("autoScrollTitle", true);
setTitleScrolling();
ColorTheme.instance().readColors(settings);
cb.setColors();
Util.overrideFonts(findViewById(android.R.id.content));
// End Visible Options
custom1ButtonActions.readPrefs(settings, actionFactory);
custom2ButtonActions.readPrefs(settings, actionFactory);
custom3ButtonActions.readPrefs(settings, actionFactory);
updateButtons();
bookOptions.filename = "";
bookOptions.maxLength = 1000000;
bookOptions.preferMainLines = false;
bookOptions.tournamentMode = false;
bookOptions.random = (settings.getInt("bookRandom", 500) - 500) * (3.0 / 500);
setBookOptions();
engineOptions.hashMB = 16;
engineOptions.hints = false;
engineOptions.hintsEdit = false;
engineOptions.rootProbe = true;
engineOptions.engineProbe = true;
String gtbPath = settings.getString("gtbPath", "").trim();
if (gtbPath.length() == 0) {
File extDir = Environment.getExternalStorageDirectory();
String sep = File.separator;
gtbPath = extDir.getAbsolutePath() + sep + gtbDefaultDir;
}
engineOptions.gtbPath = gtbPath;
setEngineOptions(false);
setEgtbHints(cb.getSelectedSquare());
updateThinkingInfo();
pgnOptions.view.variations = settings.getBoolean("viewVariations", true);
pgnOptions.view.comments = settings.getBoolean("viewComments", true);
pgnOptions.view.nag = settings.getBoolean("viewNAG", true);
pgnOptions.view.headers = settings.getBoolean("viewHeaders", false);
final int oldViewPieceType = pgnOptions.view.pieceType;
pgnOptions.view.pieceType = getIntSetting("viewPieceType", PGNOptions.PT_LOCAL);
showVariationLine = settings.getBoolean("showVariationLine", false);
pgnOptions.imp.variations = settings.getBoolean("importVariations", true);
pgnOptions.imp.comments = settings.getBoolean("importComments", true);
pgnOptions.imp.nag = settings.getBoolean("importNAG", true);
pgnOptions.exp.variations = settings.getBoolean("exportVariations", true);
pgnOptions.exp.comments = settings.getBoolean("exportComments", true);
pgnOptions.exp.nag = settings.getBoolean("exportNAG", true);
pgnOptions.exp.playerAction = settings.getBoolean("exportPlayerAction", false);
pgnOptions.exp.clockInfo = settings.getBoolean("exportTime", false);
gameTextListener.clear();
setPieceNames(pgnOptions.view.pieceType);
ctrl.prefsChanged(oldViewPieceType != pgnOptions.view.pieceType);
// update the typeset in case of a change anyway, cause it could occur
// as well in rotation
setFigurineNotation(pgnOptions.view.pieceType == PGNOptions.PT_FIGURINE, fontSize);
showMaterialDiff = settings.getBoolean("materialDiff", true);
secondTitleLine.setVisibility(showMaterialDiff ? View.VISIBLE : View.GONE);
}
}
/**
* Change the Pieces into figurine or regular (i.e. letters) display
*/
private final void setFigurineNotation(boolean displayAsFigures, int fontSize) {
if (displayAsFigures) {
// increase the font cause it has different kerning and looks small
float increaseFontSize = fontSize * 1.1f;
moveList.setTypeface(figNotation);
moveList.setTextSize(increaseFontSize);
thinking.setTypeface(figNotation);
thinking.setTextSize(increaseFontSize);
} else {
moveList.setTypeface(defaultMoveListTypeFace);
thinking.setTypeface(defaultThinkingListTypeFace);
}
}
/** Enable/disable title bar scrolling. */
private final void setTitleScrolling() {
TextUtils.TruncateAt where = autoScrollTitle ? TextUtils.TruncateAt.MARQUEE
: TextUtils.TruncateAt.END;
whiteTitleText.setEllipsize(where);
blackTitleText.setEllipsize(where);
whiteFigText.setEllipsize(where);
blackFigText.setEllipsize(where);
}
private final void updateButtons() {
boolean largeButtons = settings.getBoolean("largeButtons", false);
Resources r = getResources();
int bWidth = (int)Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 36, r.getDisplayMetrics()));
int bHeight = (int)Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, r.getDisplayMetrics()));
if (largeButtons) {
if (custom1ButtonActions.isEnabled() &&
custom2ButtonActions.isEnabled() &&
custom3ButtonActions.isEnabled()) {
Configuration config = getResources().getConfiguration();
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
bWidth = bWidth * 6 / 5;
bHeight = bHeight * 6 / 5;
} else {
bWidth = bWidth * 5 / 4;
bHeight = bHeight * 5 / 4;
}
} else {
bWidth = bWidth * 3 / 2;
bHeight = bHeight * 3 / 2;
}
}
SVG svg = SVGParser.getSVGFromResource(getResources(), R.raw.touch);
setButtonData(custom1Button, bWidth, bHeight, custom1ButtonActions.getIcon(), svg);
setButtonData(custom2Button, bWidth, bHeight, custom2ButtonActions.getIcon(), svg);
setButtonData(custom3Button, bWidth, bHeight, custom3ButtonActions.getIcon(), svg);
setButtonData(modeButton, bWidth, bHeight, R.raw.mode, svg);
setButtonData(undoButton, bWidth, bHeight, R.raw.left, svg);
setButtonData(redoButton, bWidth, bHeight, R.raw.right, svg);
}
private final void setButtonData(ImageButton button, int bWidth, int bHeight,
int svgResId, SVG touched) {
SVG svg = SVGParser.getSVGFromResource(getResources(), svgResId);
button.setBackgroundDrawable(new SVGPictureDrawable(svg));
StateListDrawable sld = new StateListDrawable();
sld.addState(new int[]{android.R.attr.state_pressed}, new SVGPictureDrawable(touched));
button.setImageDrawable(sld);
LayoutParams lp = button.getLayoutParams();
lp.height = bHeight;
lp.width = bWidth;
button.setLayoutParams(lp);
button.setPadding(0,0,0,0);
button.setScaleType(ScaleType.FIT_XY);
}
private synchronized final void setWakeLock(boolean enableLock) {
WakeLock wl = wakeLock;
if (wl != null) {
if (wl.isHeld())
wl.release();
if (enableLock)
wl.acquire();
}
}
private final void setEngineStrength(String engine, int strength) {
ctrl.setEngineStrength(engine, strength);
setEngineTitle(engine, strength);
}
private final void setEngineTitle(String engine, int strength) {
if (engine.contains("/")) {
int idx = engine.lastIndexOf('/');
String eName = engine.substring(idx + 1);
engineTitleText.setText(eName);
} else {
String eName = getString(engine.equals("cuckoochess") ?
R.string.cuckoochess_engine :
R.string.stockfish_engine);
boolean analysis = (ctrl != null) && ctrl.analysisMode();
if ((strength < 1000) && !analysis) {
engineTitleText.setText(String.format(Locale.US, "%s: %d%%", eName, strength / 10));
} else {
engineTitleText.setText(eName);
}
}
}
/** Update center field in second header line. */
public final void updateTimeControlTitle() {
int[] tmpInfo = ctrl.getTimeLimit();
StringBuilder sb = new StringBuilder();
int tc = tmpInfo[0];
int mps = tmpInfo[1];
int inc = tmpInfo[2];
if (mps > 0) {
sb.append(mps);
sb.append("/");
}
sb.append(timeToString(tc));
if ((inc > 0) || (mps <= 0)) {
sb.append("+");
sb.append(tmpInfo[2] / 1000);
}
summaryTitleText.setText(sb.toString());
}
@Override
public void updateEngineTitle() {
String engine = settings.getString("engine", "stockfish");
int strength = settings.getInt("strength", 1000);
setEngineTitle(engine, strength);
}
@Override
public void updateMaterialDifferenceTitle(Util.MaterialDiff diff) {
whiteFigText.setText(diff.white);
blackFigText.setText(diff.black);
}
private final void setBookOptions() {
BookOptions options = new BookOptions(bookOptions);
if (options.filename.length() > 0) {
File extDir = Environment.getExternalStorageDirectory();
String sep = File.separator;
options.filename = extDir.getAbsolutePath() + sep + bookDir + sep + options.filename;
}
ctrl.setBookOptions(options);
}
private boolean egtbForceReload = false;
private final void setEngineOptions(boolean restart) {
computeNetEngineID();
ctrl.setEngineOptions(new EngineOptions(engineOptions), restart);
Probe.getInstance().setPath(engineOptions.gtbPath, egtbForceReload);
egtbForceReload = false;
}
private final void computeNetEngineID() {
String id = "";
try {
String engine = settings.getString("engine", "stockfish");
String[] lines = Util.readFile(engine);
if (lines.length >= 3)
id = lines[1] + ":" + lines[2];
} catch (IOException e) {
}
engineOptions.networkID = id;
}
private final void setEgtbHints(int sq) {
if (!engineOptions.hints || (sq < 0)) {
cb.setSquareDecorations(null);
return;
}
Probe gtbProbe = Probe.getInstance();
ArrayList<Pair<Integer, Integer>> x = gtbProbe.movePieceProbe(cb.pos, sq);
if (x == null) {
cb.setSquareDecorations(null);
return;
}
ArrayList<SquareDecoration> sd = new ArrayList<SquareDecoration>();
for (Pair<Integer,Integer> p : x)
sd.add(new SquareDecoration(p.first, p.second));
cb.setSquareDecorations(sd);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options_menu, menu);
if(isSinglePlayer)
return true;
else
return false;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.item_file_menu);
item.setTitle(boardGestures ? R.string.option_file : R.string.tools_menu);
if(isSinglePlayer)
return true;
else
return false;
}
static private final int RESULT_EDITBOARD = 0;
static private final int RESULT_SETTINGS = 1;
static private final int RESULT_LOAD_PGN = 2;
static private final int RESULT_LOAD_FEN = 3;
static private final int RESULT_SELECT_SCID = 4;
static private final int RESULT_OI_PGN_SAVE = 5;
static private final int RESULT_OI_PGN_LOAD = 6;
static private final int RESULT_OI_FEN_LOAD = 7;
static private final int RESULT_GET_FEN = 8;
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.item_new_game:
showDialog(NEW_GAME_DIALOG);
return true;
case R.id.item_editboard: {
startEditBoard(ctrl.getFEN());
return true;
}
case R.id.item_settings: {
Intent i = new Intent(DroidFish.this, Preferences.class);
startActivityForResult(i, RESULT_SETTINGS);
return true;
}
case R.id.item_file_menu: {
int dialog = boardGestures ? FILE_MENU_DIALOG : BOARD_MENU_DIALOG;
removeDialog(dialog);
showDialog(dialog);
return true;
}
case R.id.item_goto_move: {
showDialog(SELECT_MOVE_DIALOG);
return true;
}
case R.id.item_force_move: {
ctrl.stopSearch();
return true;
}
case R.id.item_draw: {
if (ctrl.humansTurn()) {
if (ctrl.claimDrawIfPossible()) {
ctrl.stopPonder();
} else {
Toast.makeText(getApplicationContext(), R.string.offer_draw, Toast.LENGTH_SHORT).show();
}
}
return true;
}
case R.id.item_resign: {
if (ctrl.humansTurn()) {
ctrl.resignGame();
}
return true;
}
case R.id.select_book:
removeDialog(SELECT_BOOK_DIALOG);
showDialog(SELECT_BOOK_DIALOG);
return true;
case R.id.manage_engines:
showDialog(MANAGE_ENGINES_DIALOG);
return true;
case R.id.set_color_theme:
showDialog(SET_COLOR_THEME_DIALOG);
return true;
case R.id.item_about:
showDialog(ABOUT_DIALOG);
return true;
}
return false;
}
private void startEditBoard(String fen) {
Intent i = new Intent(DroidFish.this, EditBoard.class);
i.setAction(fen);
startActivityForResult(i, RESULT_EDITBOARD);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RESULT_SETTINGS:
handlePrefsChange();
break;
case RESULT_EDITBOARD:
if (resultCode == RESULT_OK) {
try {
String fen = data.getAction();
ctrl.setFENOrPGN(fen);
setBoardFlip(false);
} catch (ChessParseError e) {
}
}
break;
case RESULT_LOAD_PGN:
if (resultCode == RESULT_OK) {
try {
String pgn = data.getAction();
int modeNr = ctrl.getGameMode().getModeNr();
if ((modeNr != GameMode.ANALYSIS) && (modeNr != GameMode.EDIT_GAME))
newGameMode(GameMode.EDIT_GAME);
ctrl.setFENOrPGN(pgn);
setBoardFlip(true);
} catch (ChessParseError e) {
Toast.makeText(getApplicationContext(), getParseErrString(e), Toast.LENGTH_SHORT).show();
}
}
break;
case RESULT_SELECT_SCID:
if (resultCode == RESULT_OK) {
String pathName = data.getAction();
if (pathName != null) {
Editor editor = settings.edit();
editor.putString("currentScidFile", pathName);
editor.putInt("currFT", FT_SCID);
editor.commit();
Intent i = new Intent(DroidFish.this, LoadScid.class);
i.setAction("com.if3games.chessonline.loadScid");
i.putExtra("com.if3games.chessonline.pathname", pathName);
startActivityForResult(i, RESULT_LOAD_PGN);
}
}
break;
case RESULT_OI_PGN_LOAD:
if (resultCode == RESULT_OK) {
String pathName = getFilePathFromUri(data.getData());
if (pathName != null)
loadPGNFromFile(pathName);
}
break;
case RESULT_OI_PGN_SAVE:
if (resultCode == RESULT_OK) {
String pathName = getFilePathFromUri(data.getData());
if (pathName != null) {
if ((pathName.length() > 0) && !pathName.contains("."))
pathName += ".pgn";
savePGNToFile(pathName, false);
}
}
break;
case RESULT_OI_FEN_LOAD:
if (resultCode == RESULT_OK) {
String pathName = getFilePathFromUri(data.getData());
if (pathName != null)
loadFENFromFile(pathName);
}
break;
case RESULT_GET_FEN:
if (resultCode == RESULT_OK) {
String fen = data.getStringExtra(Intent.EXTRA_TEXT);
if (fen == null) {
String pathName = getFilePathFromUri(data.getData());
loadFENFromFile(pathName);
}
setFenHelper(fen);
}
break;
case RESULT_LOAD_FEN:
if (resultCode == RESULT_OK) {
String fen = data.getAction();
setFenHelper(fen);
}
break;
// GMS
case RC_SELECT_PLAYERS:
// we got the result from the "select players" UI -- ready to create the room
handleSelectPlayersResult(resultCode, data, gmsGameVariantNumber);
break;
case RC_INVITATION_INBOX:
// we got the result from the "select invitation" UI (invitation inbox). We're
// ready to accept the selected invitation:
handleInvitationInboxResult(resultCode, data);
break;
case RC_WAITING_ROOM:
// we got the result from the "waiting room" UI.
if (resultCode == Activity.RESULT_OK) {
// ready to start playing
//Log.d(TAG, "Starting game (waiting room returned OK).");
//if(!imNotFirst)
//sendImFirstLevelNumberForStart();
if(gmsGameVariantNumber != -1)
startGame(true, gmsGameVariantNumber);
} else if (resultCode == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
// player indicated that they want to leave the room
leaveRoom();
} else if (resultCode == Activity.RESULT_CANCELED) {
// Dialog was cancelled (user pressed back key, for instance). In our game,
// this means leaving the room too. In more elaborate games, this could mean
// something else (like minimizing the waiting room UI).
leaveRoom();
}
break;
}
}
/** Set new game mode. */
private final void newGameMode(int gameModeType) {
Editor editor = settings.edit();
String gameModeStr = String.format(Locale.US, "%d", gameModeType);
editor.putString("gameMode", gameModeStr);
editor.commit();
gameMode = new GameMode(gameModeType);
ctrl.setGameMode(gameMode);
}
public static String getFilePathFromUri(Uri uri) {
if (uri == null)
return null;
return uri.getPath();
}
private final String getParseErrString(ChessParseError e) {
if (e.resourceId == -1)
return e.getMessage();
else
return getString(e.resourceId);
}
private final int nameMatchScore(String name, String match) {
if (name == null)
return 0;
String lName = name.toLowerCase(Locale.US);
String lMatch = match.toLowerCase(Locale.US);
if (name.equals(match))
return 6;
if (lName.equals(lMatch))
return 5;
if (name.startsWith(match))
return 4;
if (lName.startsWith(lMatch))
return 3;
if (name.contains(match))
return 2;
if (lName.contains(lMatch))
return 1;
return 0;
}
private final void setBoardFlip() {
setBoardFlip(false);
}
/** Set a boolean preference setting. */
private final void setBooleanPref(String name, boolean value) {
Editor editor = settings.edit();
editor.putBoolean(name, value);
editor.commit();
}
/** Toggle a boolean preference setting. Return new value. */
private final boolean toggleBooleanPref(String name) {
boolean value = !settings.getBoolean(name, false);
setBooleanPref(name, value);
return value;
}
private final void setBoardFlip(boolean matchPlayerNames) {
boolean flipped = boardFlipped;
if (playerNameFlip && matchPlayerNames && (ctrl != null)) {
final TreeMap<String,String> headers = new TreeMap<String,String>();
ctrl.getHeaders(headers);
int whiteMatch = nameMatchScore(headers.get("White"), playerName);
int blackMatch = nameMatchScore(headers.get("Black"), playerName);
if (( flipped && (whiteMatch > blackMatch)) ||
(!flipped && (whiteMatch < blackMatch))) {
flipped = !flipped;
boardFlipped = flipped;
setBooleanPref("boardFlipped", flipped);
}
}
if (autoSwapSides) {
if (gameMode.analysisMode()) {
flipped = !cb.pos.whiteMove;
} else if (gameMode.playerWhite() && gameMode.playerBlack()) {
flipped = !cb.pos.whiteMove;
} else if (gameMode.playerWhite()) {
flipped = false;
} else if (gameMode.playerBlack()) {
flipped = true;
} else { // two computers
flipped = !cb.pos.whiteMove;
}
}
cb.setFlipped(flipped);
}
@Override
public void setSelection(int sq) {
cb.setSelection(cb.highlightLastMove ? sq : -1);
cb.userSelectedSquare = false;
setEgtbHints(sq);
}
@Override
public void setStatus(GameStatus s) {
String str;
switch (s.state) {
case ALIVE:
str = Integer.valueOf(s.moveNr).toString();
if (s.white)
str += ". " + getString(R.string.whites_move);
else
str += "... " + getString(R.string.blacks_move);
if (s.ponder) str += " (" + getString(R.string.ponder) + ")";
if (s.thinking) str += " (" + getString(R.string.thinking) + ")";
if (s.analyzing) str += " (" + getString(R.string.analyzing) + ")";
break;
case WHITE_MATE:
str = getString(R.string.white_mate);
if(!isSinglePlayer) {
if(imFirstType == 0)
handleGMSMatchComplete(ConstantsData.GAME_LOSS);
else
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
break;
case BLACK_MATE:
str = getString(R.string.black_mate);
if(imFirstType == 1)
handleGMSMatchComplete(ConstantsData.GAME_LOSS);
else
handleGMSMatchComplete(ConstantsData.GAME_WON);
break;
case WHITE_STALEMATE:
str = getString(R.string.stalemate);
if(!isSinglePlayer)
handleGMSMatchComplete(ConstantsData.GAME_DRAW);
case BLACK_STALEMATE:
str = getString(R.string.stalemate);
if(!isSinglePlayer)
handleGMSMatchComplete(ConstantsData.GAME_DRAW);
break;
case DRAW_REP: {
str = getString(R.string.draw_rep);
if (s.drawInfo.length() > 0)
str = str + " [" + s.drawInfo + "]";
if(!isSinglePlayer)
handleGMSMatchComplete(ConstantsData.GAME_DRAW);
break;
}
case DRAW_50: {
str = getString(R.string.draw_50);
if (s.drawInfo.length() > 0)
str = str + " [" + s.drawInfo + "]";
break;
}
case DRAW_NO_MATE:
str = getString(R.string.draw_no_mate);
if(!isSinglePlayer)
handleGMSMatchComplete(ConstantsData.GAME_DRAW);
break;
case DRAW_AGREE:
str = getString(R.string.draw_agree);
if(!isSinglePlayer)
handleGMSMatchComplete(ConstantsData.GAME_DRAW);
break;
case RESIGN_WHITE:
str = getString(R.string.resign_white);
if(isSinglePlayer) {
str = getString(R.string.resign_white);
} else {
if(!myTurn && (imFirstType == 1) && isOpponentResign) {
str = getString(R.string.resign_white);
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
else if(!myTurn && (imFirstType == 1) && isOpponentTimeOut) {
str = getString(R.string.gms_black_win_time);
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
else if(!myTurn && (imFirstType == 1)) {
str = getString(R.string.resign_black);
handleGMSMatchComplete(ConstantsData.GAME_LOSS);
}
else if(myTurn && (imFirstType == 0) && isOpponentResign) {
str = getString(R.string.resign_black);
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
else if(myTurn && (imFirstType == 0)) {
if((mSecondsLeft <= 0) && (gmsGameVariantNumber != ConstantsData.GAME_VARIANT_LONG))
str = getString(R.string.gms_black_win_time);
else
str = getString(R.string.resign_white);
handleGMSMatchComplete(ConstantsData.GAME_LOSS);
}
}
break;
case RESIGN_BLACK:
str = getString(R.string.resign_black);
if(isSinglePlayer) {
str = getString(R.string.resign_black);
} else {
if(!myTurn && (imFirstType == 0) && isOpponentResign) {
str = getString(R.string.resign_black);
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
else if(!myTurn && (imFirstType == 0) && isOpponentTimeOut) {
str = getString(R.string.gms_white_win_time);
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
else if(!myTurn && (imFirstType == 0)) {
str = getString(R.string.resign_white);
handleGMSMatchComplete(ConstantsData.GAME_LOSS);
}
else if(myTurn && (imFirstType == 1) && isOpponentResign) {
str = getString(R.string.resign_white);
handleGMSMatchComplete(ConstantsData.GAME_WON);
}
else if(myTurn && (imFirstType == 1)) {
if((mSecondsLeft <= 0) && (gmsGameVariantNumber != ConstantsData.GAME_VARIANT_LONG))
str = getString(R.string.gms_white_win_time);
else
str = getString(R.string.resign_black);
handleGMSMatchComplete(ConstantsData.GAME_LOSS);
}
}
break;
default:
throw new RuntimeException();
}
setStatusString(str);
}
private final void setStatusString(String str) {
status.setText(str);
}
@Override
public void moveListUpdated() {
moveList.setText(gameTextListener.getSpannableData());
Layout layout = moveList.getLayout();
if (layout != null) {
int currPos = gameTextListener.getCurrPos();
int line = layout.getLineForOffset(currPos);
int y = (int) ((line - 1.5) * moveList.getLineHeight());
moveListScroll.scrollTo(0, y);
}
}
@Override
public boolean whiteBasedScores() {
return mWhiteBasedScores;
}
@Override
public boolean ponderMode() {
return mPonderMode;
}
@Override
public int engineThreads() {
return mEngineThreads;
}
@Override
public Context getContext() {
return getApplicationContext();
}
@Override
public String playerName() {
return playerName;
}
@Override
public boolean discardVariations() {
return discardVariations;
}
/** Report a move made that is a candidate for GUI animation. */
public void setAnimMove(Position sourcePos, Move move, boolean forward) {
if (animateMoves && (move != null))
cb.setAnimMove(sourcePos, move, forward);
}
@Override
public void setPosition(Position pos, String variantInfo, ArrayList<Move> variantMoves) {
variantStr = variantInfo;
this.variantMoves = variantMoves;
cb.setPosition(pos);
setBoardFlip();
updateThinkingInfo();
setEgtbHints(cb.getSelectedSquare());
}
private String thinkingStr1 = "";
private String thinkingStr2 = "";
private String bookInfoStr = "";
private String variantStr = "";
private ArrayList<ArrayList<Move>> pvMoves = new ArrayList<ArrayList<Move>>();
private ArrayList<Move> bookMoves = null;
private ArrayList<Move> variantMoves = null;
@Override
public void setThinkingInfo(String pvStr, String statStr, String bookInfo,
ArrayList<ArrayList<Move>> pvMoves, ArrayList<Move> bookMoves) {
thinkingStr1 = pvStr;
thinkingStr2 = statStr;
bookInfoStr = bookInfo;
this.pvMoves = pvMoves;
this.bookMoves = bookMoves;
updateThinkingInfo();
if (ctrl.computerBusy()) {
lastComputationMillis = System.currentTimeMillis();
} else {
lastComputationMillis = 0;
}
updateNotification();
}
private final void updateThinkingInfo() {
boolean thinkingEmpty = true;
{
String s = "";
if (mShowThinking || gameMode.analysisMode()) {
s = thinkingStr1;
if (s.length() > 0) thinkingEmpty = false;
if (mShowStats) {
if (!thinkingEmpty)
s += "\n";
s += thinkingStr2;
if (s.length() > 0) thinkingEmpty = false;
}
}
thinking.setText(s, TextView.BufferType.SPANNABLE);
}
if (mShowBookHints && (bookInfoStr.length() > 0)) {
String s = "";
if (!thinkingEmpty)
s += "<br>";
s += Util.boldStart + getString(R.string.book) + Util.boldStop + bookInfoStr;
thinking.append(Html.fromHtml(s));
thinkingEmpty = false;
}
if (showVariationLine && (variantStr.indexOf(' ') >= 0)) {
String s = "";
if (!thinkingEmpty)
s += "<br>";
s += Util.boldStart + getString(R.string.variation) + Util.boldStop + variantStr;
thinking.append(Html.fromHtml(s));
thinkingEmpty = false;
}
thinking.setVisibility(thinkingEmpty ? View.GONE : View.VISIBLE);
List<Move> hints = null;
if (mShowThinking || gameMode.analysisMode()) {
ArrayList<ArrayList<Move>> pvMovesTmp = pvMoves;
if (pvMovesTmp.size() == 1) {
hints = pvMovesTmp.get(0);
} else if (pvMovesTmp.size() > 1) {
hints = new ArrayList<Move>();
for (ArrayList<Move> pv : pvMovesTmp)
if (!pv.isEmpty())
hints.add(pv.get(0));
}
}
if ((hints == null) && mShowBookHints)
hints = bookMoves;
if (((hints == null) || hints.isEmpty()) &&
(variantMoves != null) && variantMoves.size() > 1) {
hints = variantMoves;
}
if ((hints != null) && (hints.size() > maxNumArrows)) {
hints = hints.subList(0, maxNumArrows);
}
cb.setMoveHints(hints);
}
static private final int PROMOTE_DIALOG = 0;
static private final int BOARD_MENU_DIALOG = 1;
static private final int ABOUT_DIALOG = 2;
static private final int SELECT_MOVE_DIALOG = 3;
static private final int SELECT_BOOK_DIALOG = 4;
static private final int SELECT_ENGINE_DIALOG = 5;
static private final int SELECT_ENGINE_DIALOG_NOMANAGE = 6;
static private final int SELECT_PGN_FILE_DIALOG = 7;
static private final int SELECT_PGN_FILE_SAVE_DIALOG = 8;
static private final int SET_COLOR_THEME_DIALOG = 9;
static private final int GAME_MODE_DIALOG = 10;
static private final int SELECT_PGN_SAVE_NEWFILE_DIALOG = 11;
static private final int MOVELIST_MENU_DIALOG = 12;
static private final int THINKING_MENU_DIALOG = 13;
static private final int GO_BACK_MENU_DIALOG = 14;
static private final int GO_FORWARD_MENU_DIALOG = 15;
static private final int FILE_MENU_DIALOG = 16;
static private final int NEW_GAME_DIALOG = 17;
static private final int CUSTOM1_BUTTON_DIALOG = 18;
static private final int CUSTOM2_BUTTON_DIALOG = 19;
static private final int CUSTOM3_BUTTON_DIALOG = 20;
static private final int MANAGE_ENGINES_DIALOG = 21;
static private final int NETWORK_ENGINE_DIALOG = 22;
static private final int NEW_NETWORK_ENGINE_DIALOG = 23;
static private final int NETWORK_ENGINE_CONFIG_DIALOG = 24;
static private final int DELETE_NETWORK_ENGINE_DIALOG = 25;
static private final int CLIPBOARD_DIALOG = 26;
static private final int SELECT_FEN_FILE_DIALOG = 27;
// gms
static private final int NEW_GMS_GAME_DIALOG = 28;
static private final int GAME_GMS_MODE_DIALOG = 29;
static private final int GAME_GMS_DRAW_ASK = 30;
static private final int GAME_GMS_AUTOMATCH_VARIANT_OPT = 31;
static private final int GAME_GMS_INVITE_VARIANT_OPT = 32;
static private final int GAME_GMS_EXIT = 33;
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case NEW_GAME_DIALOG: return newGameDialog();
case PROMOTE_DIALOG: return promoteDialog();
case BOARD_MENU_DIALOG: return boardMenuDialog();
case FILE_MENU_DIALOG: return fileMenuDialog();
case ABOUT_DIALOG: return aboutDialog();
case SELECT_MOVE_DIALOG: return selectMoveDialog();
case SELECT_BOOK_DIALOG: return selectBookDialog();
case SELECT_ENGINE_DIALOG: return selectEngineDialog(false);
case SELECT_ENGINE_DIALOG_NOMANAGE: return selectEngineDialog(true);
case SELECT_PGN_FILE_DIALOG: return selectPgnFileDialog();
case SELECT_PGN_FILE_SAVE_DIALOG: return selectPgnFileSaveDialog();
case SELECT_PGN_SAVE_NEWFILE_DIALOG: return selectPgnSaveNewFileDialog();
case SET_COLOR_THEME_DIALOG: return setColorThemeDialog();
case GAME_MODE_DIALOG: return gameModeDialog();
case MOVELIST_MENU_DIALOG: return moveListMenuDialog();
case THINKING_MENU_DIALOG: return thinkingMenuDialog();
case GO_BACK_MENU_DIALOG: return goBackMenuDialog();
case GO_FORWARD_MENU_DIALOG: return goForwardMenuDialog();
case CUSTOM1_BUTTON_DIALOG: return makeButtonDialog(custom1ButtonActions);
case CUSTOM2_BUTTON_DIALOG: return makeButtonDialog(custom2ButtonActions);
case CUSTOM3_BUTTON_DIALOG: return makeButtonDialog(custom3ButtonActions);
case MANAGE_ENGINES_DIALOG: return manageEnginesDialog();
case NETWORK_ENGINE_DIALOG: return networkEngineDialog();
case NEW_NETWORK_ENGINE_DIALOG: return newNetworkEngineDialog();
case NETWORK_ENGINE_CONFIG_DIALOG: return networkEngineConfigDialog();
case DELETE_NETWORK_ENGINE_DIALOG: return deleteNetworkEngineDialog();
case CLIPBOARD_DIALOG: return clipBoardDialog();
case SELECT_FEN_FILE_DIALOG: return selectFenFileDialog();
case NEW_GMS_GAME_DIALOG: return newGmsGameDialog();
case GAME_GMS_MODE_DIALOG: return gameGmsModeDialog();
case GAME_GMS_DRAW_ASK: return drawGmsGameDialog();
case GAME_GMS_AUTOMATCH_VARIANT_OPT: return gameGmsAutoMatchVariantDialog();
case GAME_GMS_INVITE_VARIANT_OPT: return gameGmsInviteVariantDialog();
case GAME_GMS_EXIT: return exitGmsGameDialog();
}
return null;
}
private final Dialog newGameDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.option_new_game);
builder.setMessage(R.string.start_new_game);
builder.setPositiveButton(R.string.yes, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startNewGame(2);
}
});
builder.setNeutralButton(R.string.white, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startNewGame(0);
}
});
builder.setNegativeButton(R.string.black, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startNewGame(1);
}
});
return builder.create();
}
private final void startNewGame(int type) {
if (type != 2) {
int gameModeType = (type == 0) ? GameMode.PLAYER_WHITE : GameMode.PLAYER_BLACK;
Editor editor = settings.edit();
String gameModeStr = String.format(Locale.US, "%d", gameModeType);
editor.putString("gameMode", gameModeStr);
editor.commit();
gameMode = new GameMode(gameModeType);
}
// savePGNToFile(".autosave.pgn", true);
TimeControlData tcData = new TimeControlData();
tcData.setTimeControl(timeControl, movesPerSession, timeIncrement);
ctrl.newGame(gameMode, tcData);
ctrl.startGame();
setBoardFlip(true);
updateEngineTitle();
}
private final Dialog promoteDialog() {
final CharSequence[] items = {
getString(R.string.queen), getString(R.string.rook),
getString(R.string.bishop), getString(R.string.knight)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.promote_pawn_to);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
ctrl.reportPromotePiece(item);
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog clipBoardDialog() {
final int COPY_GAME = 0;
final int COPY_POSITION = 1;
final int PASTE = 2;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
lst.add(getString(R.string.copy_game)); actions.add(COPY_GAME);
lst.add(getString(R.string.copy_position)); actions.add(COPY_POSITION);
lst.add(getString(R.string.paste)); actions.add(PASTE);
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.tools_menu);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case COPY_GAME: {
String pgn = ctrl.getPGN();
ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(pgn);
break;
}
case COPY_POSITION: {
String fen = ctrl.getFEN() + "\n";
ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(fen);
break;
}
case PASTE: {
ClipboardManager clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
if (clipboard.hasText()) {
String fenPgn = clipboard.getText().toString();
try {
ctrl.setFENOrPGN(fenPgn);
setBoardFlip(true);
} catch (ChessParseError e) {
Toast.makeText(getApplicationContext(), getParseErrString(e), Toast.LENGTH_SHORT).show();
}
}
break;
}
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog boardMenuDialog() {
final int CLIPBOARD = 0;
final int FILEMENU = 1;
final int SHARE = 2;
final int GET_FEN = 3;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
lst.add(getString(R.string.clipboard)); actions.add(CLIPBOARD);
lst.add(getString(R.string.option_file)); actions.add(FILEMENU);
lst.add(getString(R.string.share)); actions.add(SHARE);
if (hasFenProvider(getPackageManager())) {
lst.add(getString(R.string.get_fen)); actions.add(GET_FEN);
}
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.tools_menu);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case CLIPBOARD: {
showDialog(CLIPBOARD_DIALOG);
break;
}
case FILEMENU: {
removeDialog(FILE_MENU_DIALOG);
showDialog(FILE_MENU_DIALOG);
break;
}
case SHARE: {
shareGame();
break;
}
case GET_FEN:
getFen();
break;
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final void shareGame() {
Intent i = new Intent(Intent.ACTION_SEND);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
i.setType("text/plain");
//i.putExtra(Intent.EXTRA_TEXT, ctrl.getPGN());
i.putExtra(Intent.EXTRA_TEXT, getString(R.string.app_name) + " " + ConstantsData.MARKET_URL_HTTP);
startActivity(Intent.createChooser(i, getString(R.string.share_pgn_game)));
}
private final Dialog fileMenuDialog() {
final int LOAD_LAST_FILE = 0;
final int LOAD_GAME = 1;
final int LOAD_POS = 2;
final int LOAD_SCID_GAME = 3;
final int SAVE_GAME = 4;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
if (currFileType() != FT_NONE) {
lst.add(getString(R.string.load_last_file)); actions.add(LOAD_LAST_FILE);
}
lst.add(getString(R.string.load_game)); actions.add(LOAD_GAME);
lst.add(getString(R.string.load_position)); actions.add(LOAD_POS);
if (hasScidProvider()) {
lst.add(getString(R.string.load_scid_game)); actions.add(LOAD_SCID_GAME);
}
lst.add(getString(R.string.save_game)); actions.add(SAVE_GAME);
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.load_save_menu);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case LOAD_LAST_FILE:
loadLastFile();
break;
case LOAD_GAME:
selectFile(R.string.select_pgn_file, R.string.pgn_load, "currentPGNFile", pgnDir,
SELECT_PGN_FILE_DIALOG, RESULT_OI_PGN_LOAD);
break;
case SAVE_GAME:
selectFile(R.string.select_pgn_file_save, R.string.pgn_save, "currentPGNFile", pgnDir,
SELECT_PGN_FILE_SAVE_DIALOG, RESULT_OI_PGN_SAVE);
break;
case LOAD_POS:
selectFile(R.string.select_fen_file, R.string.pgn_load, "currentFENFile", fenDir,
SELECT_FEN_FILE_DIALOG, RESULT_OI_FEN_LOAD);
break;
case LOAD_SCID_GAME:
selectScidFile();
break;
}
}
});
AlertDialog alert = builder.create();
return alert;
}
/** Open dialog to select a game/position from the last used file. */
final private void loadLastFile() {
String path = currPathName();
if (path.length() == 0)
return;
switch (currFileType()) {
case FT_PGN:
loadPGNFromFile(path);
break;
case FT_SCID: {
Intent data = new Intent(path);
onActivityResult(RESULT_SELECT_SCID, RESULT_OK, data);
break;
}
case FT_FEN:
loadFENFromFile(path);
break;
}
}
private final Dialog aboutDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
String title = getString(R.string.app_name);
WebView wv = new WebView(this);
builder.setView(wv);
InputStream is = getResources().openRawResource(R.raw.about);
String data = Util.readFromStream(is);
if (data == null)
data = "";
try { is.close(); } catch (IOException e1) {}
wv.loadDataWithBaseURL(null, data, "text/html", "utf-8", null);
try {
PackageInfo pi = getPackageManager().getPackageInfo("com.if3games.chessonline", 0);
title += " " + pi.versionName;
} catch (NameNotFoundException e) {
}
builder.setTitle(title);
AlertDialog alert = builder.create();
return alert;
}
private final Dialog selectMoveDialog() {
View content = View.inflate(this, R.layout.select_move_number, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content);
builder.setTitle(R.string.goto_move);
final EditText moveNrView = (EditText)content.findViewById(R.id.selmove_number);
moveNrView.setText("1");
final Runnable gotoMove = new Runnable() {
public void run() {
try {
int moveNr = Integer.parseInt(moveNrView.getText().toString());
ctrl.gotoMove(moveNr);
} catch (NumberFormatException nfe) {
Toast.makeText(getApplicationContext(), R.string.invalid_number_format, Toast.LENGTH_SHORT).show();
}
}
};
builder.setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
gotoMove.run();
}
});
builder.setNegativeButton(R.string.cancel, null);
final AlertDialog dialog = builder.create();
moveNrView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
gotoMove.run();
dialog.cancel();
return true;
}
return false;
}
});
return dialog;
}
private final Dialog selectBookDialog() {
String[] fileNames = findFilesInDirectory(bookDir, new FileNameFilter() {
@Override
public boolean accept(String filename) {
int dotIdx = filename.lastIndexOf(".");
if (dotIdx < 0)
return false;
String ext = filename.substring(dotIdx+1);
return (ext.equals("ctg") || ext.equals("bin"));
}
});
final int numFiles = fileNames.length;
CharSequence[] items = new CharSequence[numFiles + 1];
for (int i = 0; i < numFiles; i++)
items[i] = fileNames[i];
items[numFiles] = getString(R.string.internal_book);
final CharSequence[] finalItems = items;
int defaultItem = numFiles;
for (int i = 0; i < numFiles; i++) {
if (bookOptions.filename.equals(items[i])) {
defaultItem = i;
break;
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_opening_book_file);
builder.setSingleChoiceItems(items, defaultItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
Editor editor = settings.edit();
String bookFile = "";
if (item < numFiles)
bookFile = finalItems[item].toString();
editor.putString("bookFile", bookFile);
editor.commit();
bookOptions.filename = bookFile;
setBookOptions();
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
return alert;
}
private final static boolean internalEngine(String name) {
return "cuckoochess".equals(name) ||
"stockfish".equals(name);
}
private final Dialog selectEngineDialog(final boolean abortOnCancel) {
String[] fileNames = findFilesInDirectory(engineDir, new FileNameFilter() {
@Override
public boolean accept(String filename) {
return !internalEngine(filename);
}
});
final int numFiles = fileNames.length;
boolean haveSf = EngineUtil.internalStockFishName() != null;
final int nEngines = numFiles + 1 + (haveSf ? 1 : 0);
final String[] items = new String[nEngines];
final String[] ids = new String[nEngines];
int idx = 0;
if (haveSf) {
ids[idx] = "stockfish"; items[idx] = getString(R.string.stockfish_engine); idx++;
}
ids[idx] = "cuckoochess"; items[idx] = getString(R.string.cuckoochess_engine); idx++;
String sep = File.separator;
String base = Environment.getExternalStorageDirectory() + sep + engineDir + sep;
for (int i = 0; i < numFiles; i++) {
ids[idx] = base + fileNames[i];
items[idx] = fileNames[i];
idx++;
}
String currEngine = ctrl.getEngine();
int defaultItem = 0;
for (int i = 0; i < nEngines; i++) {
if (ids[i].equals(currEngine)) {
defaultItem = i;
break;
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_chess_engine);
builder.setSingleChoiceItems(items, defaultItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if ((item < 0) || (item >= nEngines))
return;
Editor editor = settings.edit();
String engine = ids[item];
editor.putString("engine", engine);
editor.commit();
dialog.dismiss();
int strength = settings.getInt("strength", 1000);
setEngineOptions(false);
setEngineStrength(engine, strength);
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
if (!abortOnCancel) {
removeDialog(MANAGE_ENGINES_DIALOG);
showDialog(MANAGE_ENGINES_DIALOG);
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private static interface Loader {
void load(String pathName);
}
private final Dialog selectPgnFileDialog() {
return selectFileDialog(pgnDir, R.string.select_pgn_file, R.string.no_pgn_files,
"currentPGNFile", new Loader() {
@Override
public void load(String pathName) {
loadPGNFromFile(pathName);
}
});
}
private final Dialog selectFenFileDialog() {
return selectFileDialog(fenDir, R.string.select_fen_file, R.string.no_fen_files,
"currentFENFile", new Loader() {
@Override
public void load(String pathName) {
loadFENFromFile(pathName);
}
});
}
private final Dialog selectFileDialog(final String defaultDir, int selectFileMsg, int noFilesMsg,
String settingsName, final Loader loader) {
final String[] fileNames = findFilesInDirectory(defaultDir, null);
final int numFiles = fileNames.length;
if (numFiles == 0) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.app_name).setMessage(noFilesMsg);
AlertDialog alert = builder.create();
return alert;
}
int defaultItem = 0;
String currentFile = settings.getString(settingsName, "");
currentFile = new File(currentFile).getName();
for (int i = 0; i < numFiles; i++) {
if (currentFile.equals(fileNames[i])) {
defaultItem = i;
break;
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(selectFileMsg);
builder.setSingleChoiceItems(fileNames, defaultItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
dialog.dismiss();
String sep = File.separator;
String fn = fileNames[item].toString();
String pathName = Environment.getExternalStorageDirectory() + sep + defaultDir + sep + fn;
loader.load(pathName);
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog selectPgnFileSaveDialog() {
final String[] fileNames = findFilesInDirectory(pgnDir, null);
final int numFiles = fileNames.length;
int defaultItem = 0;
String currentPGNFile = settings.getString("currentPGNFile", "");
currentPGNFile = new File(currentPGNFile).getName();
for (int i = 0; i < numFiles; i++) {
if (currentPGNFile.equals(fileNames[i])) {
defaultItem = i;
break;
}
}
CharSequence[] items = new CharSequence[numFiles + 1];
for (int i = 0; i < numFiles; i++)
items[i] = fileNames[i];
items[numFiles] = getString(R.string.new_file);
final CharSequence[] finalItems = items;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_pgn_file_save);
builder.setSingleChoiceItems(finalItems, defaultItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
String pgnFile;
if (item >= numFiles) {
dialog.dismiss();
showDialog(SELECT_PGN_SAVE_NEWFILE_DIALOG);
} else {
dialog.dismiss();
pgnFile = fileNames[item].toString();
String sep = File.separator;
String pathName = Environment.getExternalStorageDirectory() + sep + pgnDir + sep + pgnFile;
savePGNToFile(pathName, false);
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog selectPgnSaveNewFileDialog() {
View content = View.inflate(this, R.layout.create_pgn_file, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content);
builder.setTitle(R.string.select_pgn_file_save);
final EditText fileNameView = (EditText)content.findViewById(R.id.create_pgn_filename);
fileNameView.setText("");
final Runnable savePGN = new Runnable() {
public void run() {
String pgnFile = fileNameView.getText().toString();
if ((pgnFile.length() > 0) && !pgnFile.contains("."))
pgnFile += ".pgn";
String sep = File.separator;
String pathName = Environment.getExternalStorageDirectory() + sep + pgnDir + sep + pgnFile;
savePGNToFile(pathName, false);
}
};
builder.setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
savePGN.run();
}
});
builder.setNegativeButton(R.string.cancel, null);
final Dialog dialog = builder.create();
fileNameView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
savePGN.run();
dialog.cancel();
return true;
}
return false;
}
});
return dialog;
}
private final Dialog setColorThemeDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_color_theme);
String[] themeNames = new String[ColorTheme.themeNames.length];
for (int i = 0; i < themeNames.length; i++)
themeNames[i] = getString(ColorTheme.themeNames[i]);
builder.setSingleChoiceItems(themeNames, -1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
ColorTheme.instance().setTheme(settings, item);
cb.setColors();
gameTextListener.clear();
ctrl.prefsChanged(false);
dialog.dismiss();
Util.overrideFonts(findViewById(android.R.id.content));
}
});
return builder.create();
}
private final Dialog gameModeDialog() {
final CharSequence[] items = {
getString(R.string.analysis_mode),
getString(R.string.edit_replay_game),
getString(R.string.play_white),
getString(R.string.play_black),
getString(R.string.two_players),
getString(R.string.comp_vs_comp)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_game_mode);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
int gameModeType = -1;
/* only flip site in case the player was specified resp. changed */
boolean flipSite = false;
switch (item) {
case 0: gameModeType = GameMode.ANALYSIS; break;
case 1: gameModeType = GameMode.EDIT_GAME; break;
case 2: gameModeType = GameMode.PLAYER_WHITE; flipSite = true; break;
case 3: gameModeType = GameMode.PLAYER_BLACK; flipSite = true; break;
case 4: gameModeType = GameMode.TWO_PLAYERS; break;
case 5: gameModeType = GameMode.TWO_COMPUTERS; break;
default: break;
}
dialog.dismiss();
if (gameModeType >= 0) {
Editor editor = settings.edit();
String gameModeStr = String.format(Locale.US, "%d", gameModeType);
editor.putString("gameMode", gameModeStr);
editor.commit();
gameMode = new GameMode(gameModeType);
ctrl.setGameMode(gameMode);
setBoardFlip(flipSite);
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog moveListMenuDialog() {
final int EDIT_HEADERS = 0;
final int EDIT_COMMENTS = 1;
final int REMOVE_SUBTREE = 2;
final int MOVE_VAR_UP = 3;
final int MOVE_VAR_DOWN = 4;
final int ADD_NULL_MOVE = 5;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
lst.add(getString(R.string.edit_headers)); actions.add(EDIT_HEADERS);
if (ctrl.humansTurn()) {
lst.add(getString(R.string.edit_comments)); actions.add(EDIT_COMMENTS);
}
lst.add(getString(R.string.truncate_gametree)); actions.add(REMOVE_SUBTREE);
if (ctrl.numVariations() > 1) {
lst.add(getString(R.string.move_var_up)); actions.add(MOVE_VAR_UP);
lst.add(getString(R.string.move_var_down)); actions.add(MOVE_VAR_DOWN);
}
boolean allowNullMove =
gameMode.analysisMode() ||
(gameMode.playerWhite() && gameMode.playerBlack() && !gameMode.clocksActive());
if (allowNullMove) {
lst.add(getString(R.string.add_null_move)); actions.add(ADD_NULL_MOVE);
}
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.edit_game);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case EDIT_HEADERS: {
final TreeMap<String,String> headers = new TreeMap<String,String>();
ctrl.getHeaders(headers);
AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this);
builder.setTitle(R.string.edit_headers);
View content = View.inflate(DroidFish.this, R.layout.edit_headers, null);
builder.setView(content);
final TextView event, site, date, round, white, black;
event = (TextView)content.findViewById(R.id.ed_header_event);
site = (TextView)content.findViewById(R.id.ed_header_site);
date = (TextView)content.findViewById(R.id.ed_header_date);
round = (TextView)content.findViewById(R.id.ed_header_round);
white = (TextView)content.findViewById(R.id.ed_header_white);
black = (TextView)content.findViewById(R.id.ed_header_black);
event.setText(headers.get("Event"));
site .setText(headers.get("Site"));
date .setText(headers.get("Date"));
round.setText(headers.get("Round"));
white.setText(headers.get("White"));
black.setText(headers.get("Black"));
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
headers.put("Event", event.getText().toString().trim());
headers.put("Site", site .getText().toString().trim());
headers.put("Date", date .getText().toString().trim());
headers.put("Round", round.getText().toString().trim());
headers.put("White", white.getText().toString().trim());
headers.put("Black", black.getText().toString().trim());
ctrl.setHeaders(headers);
setBoardFlip(true);
}
});
builder.show();
break;
}
case EDIT_COMMENTS: {
AlertDialog.Builder builder = new AlertDialog.Builder(DroidFish.this);
builder.setTitle(R.string.edit_comments);
View content = View.inflate(DroidFish.this, R.layout.edit_comments, null);
builder.setView(content);
DroidChessController.CommentInfo commInfo = ctrl.getComments();
final TextView preComment, moveView, nag, postComment;
preComment = (TextView)content.findViewById(R.id.ed_comments_pre);
moveView = (TextView)content.findViewById(R.id.ed_comments_move);
nag = (TextView)content.findViewById(R.id.ed_comments_nag);
postComment = (TextView)content.findViewById(R.id.ed_comments_post);
preComment.setText(commInfo.preComment);
postComment.setText(commInfo.postComment);
moveView.setText(commInfo.move);
String nagStr = Node.nagStr(commInfo.nag).trim();
if ((nagStr.length() == 0) && (commInfo.nag > 0))
nagStr = String.format(Locale.US, "%d", commInfo.nag);
nag.setText(nagStr);
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String pre = preComment.getText().toString().trim();
String post = postComment.getText().toString().trim();
int nagVal = Node.strToNag(nag.getText().toString());
DroidChessController.CommentInfo commInfo = new DroidChessController.CommentInfo();
commInfo.preComment = pre;
commInfo.postComment = post;
commInfo.nag = nagVal;
ctrl.setComments(commInfo);
}
});
builder.show();
break;
}
case REMOVE_SUBTREE:
ctrl.removeSubTree();
break;
case MOVE_VAR_UP:
ctrl.moveVariation(-1);
break;
case MOVE_VAR_DOWN:
ctrl.moveVariation(1);
break;
case ADD_NULL_MOVE:
ctrl.makeHumanNullMove();
break;
}
moveListMenuDlg = null;
}
});
AlertDialog alert = builder.create();
moveListMenuDlg = alert;
return alert;
}
private final Dialog thinkingMenuDialog() {
final int ADD_ANALYSIS = 0;
final int MULTIPV_DEC = 1;
final int MULTIPV_INC = 2;
final int HIDE_STATISTICS = 3;
final int SHOW_STATISTICS = 4;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
lst.add(getString(R.string.add_analysis)); actions.add(ADD_ANALYSIS);
final int numPV = ctrl.getNumPV();
if (gameMode.analysisMode()) {
int maxPV = ctrl.maxPV();
if (numPV > 1) {
lst.add(getString(R.string.fewer_variations)); actions.add(MULTIPV_DEC);
}
if (numPV < maxPV) {
lst.add(getString(R.string.more_variations)); actions.add(MULTIPV_INC);
}
}
if (thinkingStr1.length() > 0) {
if (mShowStats) {
lst.add(getString(R.string.hide_statistics)); actions.add(HIDE_STATISTICS);
} else {
lst.add(getString(R.string.show_statistics)); actions.add(SHOW_STATISTICS);
}
}
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.analysis);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case ADD_ANALYSIS: {
ArrayList<ArrayList<Move>> pvMovesTmp = pvMoves;
String[] pvStrs = thinkingStr1.split("\n");
for (int i = 0; i < pvMovesTmp.size(); i++) {
ArrayList<Move> pv = pvMovesTmp.get(i);
StringBuilder preComment = new StringBuilder();
if (i < pvStrs.length) {
String[] tmp = pvStrs[i].split(" ");
for (int j = 0; j < 2; j++) {
if (j < tmp.length) {
if (j > 0) preComment.append(' ');
preComment.append(tmp[j]);
}
}
if (preComment.length() > 0) preComment.append(':');
}
boolean updateDefault = (i == 0);
ctrl.addVariation(preComment.toString(), pv, updateDefault);
}
break;
}
case MULTIPV_DEC:
ctrl.setMultiPVMode(numPV - 1);
break;
case MULTIPV_INC:
ctrl.setMultiPVMode(numPV + 1);
break;
case HIDE_STATISTICS:
case SHOW_STATISTICS: {
mShowStats = finalActions.get(item) == SHOW_STATISTICS;
Editor editor = settings.edit();
editor.putBoolean("showStats", mShowStats);
editor.commit();
updateThinkingInfo();
break;
}
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog goBackMenuDialog() {
final int GOTO_START_GAME = 0;
final int GOTO_START_VAR = 1;
final int GOTO_PREV_VAR = 2;
final int LOAD_PREV_GAME = 3;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
lst.add(getString(R.string.goto_start_game)); actions.add(GOTO_START_GAME);
lst.add(getString(R.string.goto_start_variation)); actions.add(GOTO_START_VAR);
if (ctrl.currVariation() > 0) {
lst.add(getString(R.string.goto_prev_variation)); actions.add(GOTO_PREV_VAR);
}
final int currFT = currFileType();
final String currPathName = currPathName();
if ((currFT != FT_NONE) && !gameMode.clocksActive()) {
lst.add(getString(R.string.load_prev_game)); actions.add(LOAD_PREV_GAME);
}
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.go_back);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case GOTO_START_GAME: ctrl.gotoMove(0); break;
case GOTO_START_VAR: ctrl.gotoStartOfVariation(); break;
case GOTO_PREV_VAR: ctrl.changeVariation(-1); break;
case LOAD_PREV_GAME:
Intent i;
if (currFT == FT_PGN) {
i = new Intent(DroidFish.this, EditPGNLoad.class);
i.setAction("com.if3games.chessonline.loadFilePrevGame");
i.putExtra("com.if3games.chessonline.pathname", currPathName);
startActivityForResult(i, RESULT_LOAD_PGN);
} else if (currFT == FT_SCID) {
i = new Intent(DroidFish.this, LoadScid.class);
i.setAction("com.if3games.chessonline.loadScidPrevGame");
i.putExtra("com.if3games.chessonline.pathname", currPathName);
startActivityForResult(i, RESULT_LOAD_PGN);
} else if (currFT == FT_FEN) {
i = new Intent(DroidFish.this, LoadFEN.class);
i.setAction("com.if3games.chessonline.loadPrevFen");
i.putExtra("com.if3games.chessonline.pathname", currPathName);
startActivityForResult(i, RESULT_LOAD_FEN);
}
break;
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog goForwardMenuDialog() {
final int GOTO_END_VAR = 0;
final int GOTO_NEXT_VAR = 1;
final int LOAD_NEXT_GAME = 2;
List<CharSequence> lst = new ArrayList<CharSequence>();
List<Integer> actions = new ArrayList<Integer>();
lst.add(getString(R.string.goto_end_variation)); actions.add(GOTO_END_VAR);
if (ctrl.currVariation() < ctrl.numVariations() - 1) {
lst.add(getString(R.string.goto_next_variation)); actions.add(GOTO_NEXT_VAR);
}
final int currFT = currFileType();
final String currPathName = currPathName();
if ((currFT != FT_NONE) && !gameMode.clocksActive()) {
lst.add(getString(R.string.load_next_game)); actions.add(LOAD_NEXT_GAME);
}
final List<Integer> finalActions = actions;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.go_forward);
builder.setItems(lst.toArray(new CharSequence[lst.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (finalActions.get(item)) {
case GOTO_END_VAR: ctrl.gotoMove(Integer.MAX_VALUE); break;
case GOTO_NEXT_VAR: ctrl.changeVariation(1); break;
case LOAD_NEXT_GAME:
Intent i;
if (currFT == FT_PGN) {
i = new Intent(DroidFish.this, EditPGNLoad.class);
i.setAction("com.if3games.chessonline.loadFileNextGame");
i.putExtra("com.if3games.chessonline.pathname", currPathName);
startActivityForResult(i, RESULT_LOAD_PGN);
} else if (currFT == FT_SCID) {
i = new Intent(DroidFish.this, LoadScid.class);
i.setAction("com.if3games.chessonline.loadScidNextGame");
i.putExtra("com.if3games.chessonline.pathname", currPathName);
startActivityForResult(i, RESULT_LOAD_PGN);
} else if (currFT == FT_FEN) {
i = new Intent(DroidFish.this, LoadFEN.class);
i.setAction("com.if3games.chessonline.loadNextFen");
i.putExtra("com.if3games.chessonline.pathname", currPathName);
startActivityForResult(i, RESULT_LOAD_FEN);
}
break;
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private Dialog makeButtonDialog(ButtonActions buttonActions) {
List<CharSequence> names = new ArrayList<CharSequence>();
final List<UIAction> actions = new ArrayList<UIAction>();
HashSet<String> used = new HashSet<String>();
for (UIAction a : buttonActions.getMenuActions()) {
if ((a != null) && a.enabled() && !used.contains(a.getId())) {
names.add(getString(a.getName()));
actions.add(a);
used.add(a.getId());
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(buttonActions.getMenuTitle());
builder.setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
UIAction a = actions.get(item);
a.run();
}
});
return builder.create();
}
private final Dialog manageEnginesDialog() {
final CharSequence[] items = {
getString(R.string.select_engine),
getString(R.string.configure_network_engine)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.option_manage_engines);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (item) {
case 0:
removeDialog(SELECT_ENGINE_DIALOG);
showDialog(SELECT_ENGINE_DIALOG);
break;
case 1:
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
break;
}
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog networkEngineDialog() {
String[] fileNames = findFilesInDirectory(engineDir, new FileNameFilter() {
@Override
public boolean accept(String filename) {
if (internalEngine(filename))
return false;
try {
InputStream inStream = new FileInputStream(filename);
InputStreamReader inFile = new InputStreamReader(inStream);
char[] buf = new char[4];
boolean ret = (inFile.read(buf) == 4) && "NETE".equals(new String(buf));
inFile.close();
return ret;
} catch (IOException e) {
return false;
}
}
});
final int numFiles = fileNames.length;
final int numItems = numFiles + 1;
final String[] items = new String[numItems];
final String[] ids = new String[numItems];
int idx = 0;
String sep = File.separator;
String base = Environment.getExternalStorageDirectory() + sep + engineDir + sep;
for (int i = 0; i < numFiles; i++) {
ids[idx] = base + fileNames[i];
items[idx] = fileNames[i];
idx++;
}
ids[idx] = ""; items[idx] = getString(R.string.new_engine); idx++;
String currEngine = ctrl.getEngine();
int defaultItem = 0;
for (int i = 0; i < numItems; i++)
if (ids[i].equals(currEngine)) {
defaultItem = i;
break;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.configure_network_engine);
builder.setSingleChoiceItems(items, defaultItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if ((item < 0) || (item >= numItems))
return;
dialog.dismiss();
if (item == numItems - 1) {
showDialog(NEW_NETWORK_ENGINE_DIALOG);
} else {
networkEngineToConfig = ids[item];
removeDialog(NETWORK_ENGINE_CONFIG_DIALOG);
showDialog(NETWORK_ENGINE_CONFIG_DIALOG);
}
}
});
builder.setOnCancelListener(new Dialog.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
removeDialog(MANAGE_ENGINES_DIALOG);
showDialog(MANAGE_ENGINES_DIALOG);
}
});
AlertDialog alert = builder.create();
return alert;
}
// Filename of network engine to configure
private String networkEngineToConfig = "";
// Ask for name of new network engine
private final Dialog newNetworkEngineDialog() {
View content = View.inflate(this, R.layout.create_network_engine, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content);
builder.setTitle(R.string.create_network_engine);
final EditText engineNameView = (EditText)content.findViewById(R.id.create_network_engine);
engineNameView.setText("");
final Runnable createEngine = new Runnable() {
public void run() {
String engineName = engineNameView.getText().toString();
String sep = File.separator;
String pathName = Environment.getExternalStorageDirectory() + sep + engineDir + sep + engineName;
File file = new File(pathName);
boolean nameOk = true;
int errMsg = -1;
if (engineName.contains("/")) {
nameOk = false;
errMsg = R.string.slash_not_allowed;
} else if (internalEngine(engineName) || file.exists()) {
nameOk = false;
errMsg = R.string.engine_name_in_use;
}
if (!nameOk) {
Toast.makeText(getApplicationContext(), errMsg, Toast.LENGTH_LONG).show();
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
return;
}
networkEngineToConfig = pathName;
removeDialog(NETWORK_ENGINE_CONFIG_DIALOG);
showDialog(NETWORK_ENGINE_CONFIG_DIALOG);
}
};
builder.setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
createEngine.run();
}
});
builder.setNegativeButton(R.string.cancel, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
builder.setOnCancelListener(new Dialog.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
final Dialog dialog = builder.create();
engineNameView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
createEngine.run();
dialog.cancel();
return true;
}
return false;
}
});
return dialog;
}
// Configure network engine settings
private final Dialog networkEngineConfigDialog() {
View content = View.inflate(this, R.layout.network_engine_config, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(content);
builder.setTitle(R.string.configure_network_engine);
final EditText hostNameView = (EditText)content.findViewById(R.id.network_engine_host);
final EditText portView = (EditText)content.findViewById(R.id.network_engine_port);
String hostName = "";
String port = "0";
try {
String[] lines = Util.readFile(networkEngineToConfig);
if ((lines.length >= 1) && lines[0].equals("NETE")) {
if (lines.length > 1)
hostName = lines[1];
if (lines.length > 2)
port = lines[2];
}
} catch (IOException e1) {
}
hostNameView.setText(hostName);
portView.setText(port);
final Runnable writeConfig = new Runnable() {
public void run() {
String hostName = hostNameView.getText().toString();
String port = portView.getText().toString();
try {
FileWriter fw = new FileWriter(new File(networkEngineToConfig), false);
fw.write("NETE\n");
fw.write(hostName); fw.write("\n");
fw.write(port); fw.write("\n");
fw.close();
setEngineOptions(true);
} catch (IOException e) {
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}
};
builder.setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
writeConfig.run();
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
builder.setNegativeButton(R.string.cancel, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
builder.setOnCancelListener(new Dialog.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
builder.setNeutralButton(R.string.delete, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
removeDialog(DELETE_NETWORK_ENGINE_DIALOG);
showDialog(DELETE_NETWORK_ENGINE_DIALOG);
}
});
final Dialog dialog = builder.create();
portView.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
writeConfig.run();
dialog.cancel();
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
return true;
}
return false;
}
});
return dialog;
}
private Dialog deleteNetworkEngineDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.delete_network_engine);
String msg = networkEngineToConfig;
if (msg.lastIndexOf('/') >= 0)
msg = msg.substring(msg.lastIndexOf('/')+1);
builder.setMessage(getString(R.string.network_engine) + ": " + msg);
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
new File(networkEngineToConfig).delete();
String engine = settings.getString("engine", "stockfish");
if (engine.equals(networkEngineToConfig)) {
engine = "stockfish";
Editor editor = settings.edit();
editor.putString("engine", engine);
editor.commit();
dialog.dismiss();
int strength = settings.getInt("strength", 1000);
setEngineOptions(false);
setEngineStrength(engine, strength);
}
dialog.cancel();
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
builder.setOnCancelListener(new Dialog.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
removeDialog(NETWORK_ENGINE_DIALOG);
showDialog(NETWORK_ENGINE_DIALOG);
}
});
AlertDialog alert = builder.create();
return alert;
}
/** Open a load/save file dialog. Uses OI file manager if available. */
private void selectFile(int titleMsg, int buttonMsg, String settingsName, String defaultDir,
int dialog, int result) {
String action = "org.openintents.action.PICK_FILE";
Intent i = new Intent(action);
String currentFile = settings.getString(settingsName, "");
String sep = File.separator;
if (!currentFile.contains(sep))
currentFile = Environment.getExternalStorageDirectory() +
sep + defaultDir + sep + currentFile;
i.setData(Uri.fromFile(new File(currentFile)));
i.putExtra("org.openintents.extra.TITLE", getString(titleMsg));
i.putExtra("org.openintents.extra.BUTTON_TEXT", getString(buttonMsg));
try {
startActivityForResult(i, result);
} catch (ActivityNotFoundException e) {
removeDialog(dialog);
showDialog(dialog);
}
}
private final boolean hasScidProvider() {
List<ProviderInfo> providers = getPackageManager().queryContentProviders(null, 0, 0);
for (ProviderInfo info : providers)
if (info.authority.equals("org.scid.database.scidprovider"))
return true;
return false;
}
private final void selectScidFile() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("org.scid.android",
"org.scid.android.SelectFileActivity"));
intent.setAction(".si4");
try {
startActivityForResult(intent, RESULT_SELECT_SCID);
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}
public final static boolean hasFenProvider(PackageManager manager) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("application/x-chess-fen");
List<ResolveInfo> resolvers = manager.queryIntentActivities(i, 0);
return (resolvers != null) && (resolvers.size() > 0);
}
private final void getFen() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("application/x-chess-fen");
try {
startActivityForResult(i, RESULT_GET_FEN);
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}
final static int FT_NONE = 0;
final static int FT_PGN = 1;
final static int FT_SCID = 2;
final static int FT_FEN = 3;
private final int currFileType() {
return settings.getInt("currFT", FT_NONE);
}
/** Return path name for the last used PGN or SCID file. */
private final String currPathName() {
int ft = settings.getInt("currFT", FT_NONE);
switch (ft) {
case FT_PGN: {
String ret = settings.getString("currentPGNFile", "");
String sep = File.separator;
if (!ret.contains(sep))
ret = Environment.getExternalStorageDirectory() + sep + pgnDir + sep + ret;
return ret;
}
case FT_SCID:
return settings.getString("currentScidFile", "");
case FT_FEN:
return settings.getString("currentFENFile", "");
default:
return "";
}
}
private static interface FileNameFilter {
boolean accept(String filename);
}
private final String[] findFilesInDirectory(String dirName, final FileNameFilter filter) {
File extDir = Environment.getExternalStorageDirectory();
String sep = File.separator;
File dir = new File(extDir.getAbsolutePath() + sep + dirName);
File[] files = dir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if (!pathname.isFile())
return false;
return (filter == null) || filter.accept(pathname.getAbsolutePath());
}
});
if (files == null)
files = new File[0];
final int numFiles = files.length;
String[] fileNames = new String[numFiles];
for (int i = 0; i < files.length; i++)
fileNames[i] = files[i].getName();
Arrays.sort(fileNames, String.CASE_INSENSITIVE_ORDER);
return fileNames;
}
/** Save current game to a PGN file. */
private final void savePGNToFile(String pathName, boolean silent) {
String pgn = ctrl.getPGN();
Editor editor = settings.edit();
editor.putString("currentPGNFile", pathName);
editor.putInt("currFT", FT_PGN);
editor.commit();
Intent i = new Intent(DroidFish.this, EditPGNSave.class);
i.setAction("com.if3games.chessonline.saveFile");
i.putExtra("com.if3games.chessonline.pathname", pathName);
i.putExtra("com.if3games.chessonline.pgn", pgn);
i.putExtra("com.if3games.chessonline.silent", silent);
startActivity(i);
}
/** Load a PGN game from a file. */
private final void loadPGNFromFile(String pathName) {
Editor editor = settings.edit();
editor.putString("currentPGNFile", pathName);
editor.putInt("currFT", FT_PGN);
editor.commit();
Intent i = new Intent(DroidFish.this, EditPGNLoad.class);
i.setAction("com.if3games.chessonline.loadFile");
i.putExtra("com.if3games.chessonline.pathname", pathName);
startActivityForResult(i, RESULT_LOAD_PGN);
}
/** Load a FEN position from a file. */
private final void loadFENFromFile(String pathName) {
if (pathName == null)
return;
Editor editor = settings.edit();
editor.putString("currentFENFile", pathName);
editor.putInt("currFT", FT_FEN);
editor.commit();
Intent i = new Intent(DroidFish.this, LoadFEN.class);
i.setAction("com.if3games.chessonline.loadFen");
i.putExtra("com.if3games.chessonline.pathname", pathName);
startActivityForResult(i, RESULT_LOAD_FEN);
}
private final void setFenHelper(String fen) {
if (fen == null)
return;
try {
ctrl.setFENOrPGN(fen);
} catch (ChessParseError e) {
// If FEN corresponds to illegal chess position, go into edit board mode.
try {
TextIO.readFEN(fen);
} catch (ChessParseError e2) {
if (e2.pos != null)
startEditBoard(fen);
}
}
}
@Override
public void requestPromotePiece() {
showDialog(PROMOTE_DIALOG);
}
@Override
public void reportInvalidMove(Move m) {
invalidMove = true;
String msg = String.format(Locale.US, "%s %s-%s",
getString(R.string.invalid_move),
TextIO.squareToString(m.from), TextIO.squareToString(m.to));
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void reportEngineName(String engine) {
String msg = String.format(Locale.US, "%s: %s",
getString(R.string.engine), engine);
if(isSinglePlayer)
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void reportEngineError(String errMsg) {
String msg = String.format(Locale.US, "%s: %s",
getString(R.string.engine_error), errMsg);
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
}
@Override
public void computerMoveMade() {
if (soundEnabled) {
if (moveSound != null)
moveSound.release();
try {
moveSound = MediaPlayer.create(this, R.raw.movesound);
if (moveSound != null)
moveSound.start();
} catch (NotFoundException ex) {
}
}
if (vibrateEnabled) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(500);
}
}
@Override
public void runOnUIThread(Runnable runnable) {
runOnUiThread(runnable);
}
/** Decide if user should be warned about heavy CPU usage. */
private final void updateNotification() {
boolean warn = false;
if (lastVisibleMillis != 0) { // GUI not visible
warn = lastComputationMillis >= lastVisibleMillis + 90000;
}
setNotification(warn);
}
private boolean notificationActive = false;
/** Set/clear the "heavy CPU usage" notification. */
private final void setNotification(boolean show) {
if (notificationActive == show)
return;
notificationActive = show;
final int cpuUsage = 1;
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager)getSystemService(ns);
if (show) {
int icon = R.drawable.icon;
CharSequence tickerText = getString(R.string.heavy_cpu_usage);
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
Context context = getApplicationContext();
CharSequence contentTitle = getString(R.string.background_processing);
CharSequence contentText = getString(R.string.lot_cpu_power);
Intent notificationIntent = new Intent(this, CPUWarning.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
mNotificationManager.notify(cpuUsage, notification);
} else {
mNotificationManager.cancel(cpuUsage);
}
}
private final String timeToString(int time) {
int secs = (int)Math.floor((time + 999) / 1000.0);
boolean neg = false;
if (secs < 0) {
neg = true;
secs = -secs;
}
int mins = secs / 60;
secs -= mins * 60;
StringBuilder ret = new StringBuilder();
if (neg) ret.append('-');
ret.append(mins);
ret.append(':');
if (secs < 10) ret.append('0');
ret.append(secs);
return ret.toString();
}
private Handler handlerTimer = new Handler();
private Runnable r = new Runnable() {
public void run() {
ctrl.updateRemainingTime();
}
};
@Override
public void setRemainingTime(int wTime, int bTime, int nextUpdate) {
if (ctrl.getGameMode().clocksActive()) {
whiteTitleText.setText(getString(R.string.white_square_character) + " " + timeToString(wTime));
blackTitleText.setText(getString(R.string.black_square_character) + " " + timeToString(bTime));
} else {
TreeMap<String,String> headers = new TreeMap<String,String>();
ctrl.getHeaders(headers);
whiteTitleText.setText(headers.get("White"));
blackTitleText.setText(headers.get("Black"));
}
handlerTimer.removeCallbacks(r);
if (nextUpdate > 0)
handlerTimer.postDelayed(r, nextUpdate);
}
/** PngTokenReceiver implementation that renders PGN data for screen display. */
static class PgnScreenText implements PgnToken.PgnTokenReceiver {
private SpannableStringBuilder sb = new SpannableStringBuilder();
private int prevType = PgnToken.EOF;
int nestLevel = 0;
boolean col0 = true;
Node currNode = null;
final static int indentStep = 15;
int currPos = 0, endPos = 0;
boolean upToDate = false;
PGNOptions options;
private static class NodeInfo {
int l0, l1;
NodeInfo(int ls, int le) {
l0 = ls;
l1 = le;
}
}
HashMap<Node, NodeInfo> nodeToCharPos;
PgnScreenText(PGNOptions options) {
nodeToCharPos = new HashMap<Node, NodeInfo>();
this.options = options;
}
public final SpannableStringBuilder getSpannableData() {
return sb;
}
public final int getCurrPos() {
return currPos;
}
public boolean isUpToDate() {
return upToDate;
}
int paraStart = 0;
int paraIndent = 0;
boolean paraBold = false;
private final void newLine() { newLine(false); }
private final void newLine(boolean eof) {
if (!col0) {
if (paraIndent > 0) {
int paraEnd = sb.length();
int indent = paraIndent * indentStep;
sb.setSpan(new LeadingMarginSpan.Standard(indent), paraStart, paraEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (paraBold) {
int paraEnd = sb.length();
sb.setSpan(new StyleSpan(Typeface.BOLD), paraStart, paraEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (!eof)
sb.append('\n');
paraStart = sb.length();
paraIndent = nestLevel;
paraBold = false;
}
col0 = true;
}
boolean pendingNewLine = false;
/** Makes moves in the move list clickable. */
private final static class MoveLink extends ClickableSpan {
private Node node;
MoveLink(Node n) {
node = n;
}
@Override
public void onClick(View widget) {
if (ctrl != null) {
// On android 4.1 this onClick method is called
// even when you long click the move list. The test
// below works around the problem.
Dialog mlmd = moveListMenuDlg;
if ((mlmd == null) || !mlmd.isShowing())
ctrl.goNode(node);
}
}
@Override
public void updateDrawState(TextPaint ds) {
}
}
public void processToken(Node node, int type, String token) {
if ( (prevType == PgnToken.RIGHT_BRACKET) &&
(type != PgnToken.LEFT_BRACKET)) {
if (options.view.headers) {
col0 = false;
newLine();
} else {
sb.clear();
paraBold = false;
}
}
if (pendingNewLine) {
if (type != PgnToken.RIGHT_PAREN) {
newLine();
pendingNewLine = false;
}
}
switch (type) {
case PgnToken.STRING:
sb.append(" \"");
sb.append(token);
sb.append('"');
break;
case PgnToken.INTEGER:
if ( (prevType != PgnToken.LEFT_PAREN) &&
(prevType != PgnToken.RIGHT_BRACKET) && !col0)
sb.append(' ');
sb.append(token);
col0 = false;
break;
case PgnToken.PERIOD:
sb.append('.');
col0 = false;
break;
case PgnToken.ASTERISK: sb.append(" *"); col0 = false; break;
case PgnToken.LEFT_BRACKET: sb.append('['); col0 = false; break;
case PgnToken.RIGHT_BRACKET: sb.append("]\n"); col0 = false; break;
case PgnToken.LEFT_PAREN:
nestLevel++;
if (col0)
paraIndent++;
newLine();
sb.append('(');
col0 = false;
break;
case PgnToken.RIGHT_PAREN:
sb.append(')');
nestLevel--;
pendingNewLine = true;
break;
case PgnToken.NAG:
sb.append(Node.nagStr(Integer.parseInt(token)));
col0 = false;
break;
case PgnToken.SYMBOL: {
if ((prevType != PgnToken.RIGHT_BRACKET) && (prevType != PgnToken.LEFT_BRACKET) && !col0)
sb.append(' ');
int l0 = sb.length();
sb.append(token);
int l1 = sb.length();
nodeToCharPos.put(node, new NodeInfo(l0, l1));
sb.setSpan(new MoveLink(node), l0, l1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (endPos < l0) endPos = l0;
col0 = false;
if (nestLevel == 0) paraBold = true;
break;
}
case PgnToken.COMMENT:
if (prevType == PgnToken.RIGHT_BRACKET) {
} else if (nestLevel == 0) {
nestLevel++;
newLine();
nestLevel--;
} else {
if ((prevType != PgnToken.LEFT_PAREN) && !col0) {
sb.append(' ');
}
}
int l0 = sb.length();
sb.append(token.replaceAll("[ \t\r\n]+", " ").trim());
int l1 = sb.length();
int color = ColorTheme.instance().getColor(ColorTheme.PGN_COMMENT);
sb.setSpan(new ForegroundColorSpan(color), l0, l1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
col0 = false;
if (nestLevel == 0)
newLine();
break;
case PgnToken.EOF:
newLine(true);
upToDate = true;
break;
}
prevType = type;
}
@Override
public void clear() {
sb.clear();
prevType = PgnToken.EOF;
nestLevel = 0;
col0 = true;
currNode = null;
currPos = 0;
endPos = 0;
nodeToCharPos.clear();
paraStart = 0;
paraIndent = 0;
paraBold = false;
pendingNewLine = false;
upToDate = false;
}
BackgroundColorSpan bgSpan = new BackgroundColorSpan(0xff888888);
@Override
public void setCurrent(Node node) {
sb.removeSpan(bgSpan);
NodeInfo ni = nodeToCharPos.get(node);
if ((ni == null) && (node != null) && (node.getParent() != null))
ni = nodeToCharPos.get(node.getParent());
if (ni != null) {
int color = ColorTheme.instance().getColor(ColorTheme.CURRENT_MOVE);
bgSpan = new BackgroundColorSpan(color);
sb.setSpan(bgSpan, ni.l0, ni.l1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
currPos = ni.l0;
} else {
currPos = 0;
}
currNode = node;
}
}
/*
* GMS Logic Initial SECTION. Methods that implement the game's multiplayer.
*/
private void startMultiplayerGameMode(int n)
{
isMatch = true;
if(mHandler != null) {
mHandler.removeCallbacks(myRunnable);
mHandler = null;
myRunnable = null;
}
if(n == 0)
myTurn = true;
else
myTurn = false;
if(n>=0 && n<=1)
startNewGame(n);
int gameModeType = -1;
/* only flip site in case the player was specified resp. changed */
boolean flipSite = false;
gameModeType = GameMode.TWO_PLAYERS;
if (gameModeType >= 0) {
Editor editor = settings.edit();
String gameModeStr = String.format(Locale.US, "%d", gameModeType);
editor.putString("gameMode", gameModeStr);
editor.commit();
GameMode gmsGameMode = new GameMode(gameModeType);
ctrl.setGameMode(gmsGameMode);
setBoardFlip(flipSite);
}
if(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_PLAYED) == 0) loadFromCloud();
else {
player1TitleText.setText(getString(R.string.str_gms_you_title) + "(" + Integer.toString(mSaveGame.getStatsFromName("rating")) + ")");
player2TitleText.setText(opponentName + "(" + Integer.toString(opponentRating) + ")");
}
if(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_PLAYED) == 0) {
player1TitleText.setText(getString(R.string.str_gms_you_title));
player2TitleText.setText(opponentName);
}
}
/*
* GMS Network SECTION. Methods that implement the game's multiplayer.
*/
@Override
public void onP2PConnected(String arg0) {
// TODO Auto-generated method stub
}
@Override
public void onP2PDisconnected(String arg0) {
// TODO Auto-generated method stub
}
@Override
public void onPeerDeclined(Room arg0, List<String> arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPeerInvitedToRoom(Room arg0, List<String> arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPeerJoined(Room arg0, List<String> arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPeerLeft(Room arg0, List<String> arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPeersConnected(Room arg0, List<String> arg1) {
// TODO Auto-generated method stub
}
@Override
public void onPeersDisconnected(Room arg0, List<String> arg1) {
// TODO Auto-generated method stub
}
@Override
public void onRoomAutoMatching(Room arg0) {
// TODO Auto-generated method stub
}
@Override
public void onRoomConnecting(Room arg0) {
// TODO Auto-generated method stub
}
@Override
public void onClick(View v) {
Intent intent;
switch (v.getId()) {
case R.id.button_sign_in:
// user wants to sign in
beginUserInitiatedSignIn();
break;
case R.id.button_sign_out:
// user wants to sign out
signOut();
switchToScreen(R.id.screen_sign_in);
break;
case R.id.button_invite_players:
if(!mAlreadyLoadedState)
loadFromCloud();
showDialog(GAME_GMS_INVITE_VARIANT_OPT);
break;
case R.id.button_see_invitations:
if(!mAlreadyLoadedState)
loadFromCloud();
// show list of pending invitations
intent = Games.Invitations.getInvitationInboxIntent(getApiClient());
switchToScreen(R.id.screen_wait);
startActivityForResult(intent, RC_INVITATION_INBOX);
break;
case R.id.button_accept_popup_invitation:
if(!mAlreadyLoadedState)
loadFromCloud();
// user wants to accept the invitation shown on the invitation popup
// (the one we got through the OnInvitationReceivedListener).
acceptInviteToRoom(mIncomingInvitationId);
mIncomingInvitationId = null;
break;
case R.id.button_quick_game:
if(!mAlreadyLoadedState)
loadFromCloud();
// user wants to play against a random opponent right now
showDialog(GAME_GMS_AUTOMATCH_VARIANT_OPT);
break;
case R.id.button_leaderboard:
startActivityForResult(Games.Leaderboards.getAllLeaderboardsIntent(getApiClient()), RC_UNUSED);
break;
case R.id.button_achive:
startActivityForResult(Games.Achievements.getAchievementsIntent(getApiClient()), RC_UNUSED);
break;
}
}
@Override
public void onSignInFailed() {
if(!isSinglePlayer) {
// Switch to screen sigh in
switchToScreen(R.id.screen_sign_in);
}
}
@Override
public void onSignInSucceeded() {
if(!isSinglePlayer) {
Games.Invitations.registerInvitationListener(getApiClient(), this);
if (getInvitationId() != null) {
acceptInviteToRoom(getInvitationId());
return;
}
if (!mAlreadyLoadedState) {
loadFromCloud();
}
displayPlayerNameScoreRank();
// Switch to screen buttons (random, invite, invite inbox)
switchToMainScreen();
}
}
void displayPlayerNameScoreRank() {
((TextView) findViewById(R.id.playerDispNameId)).setText(getString(R.string.str_gms_hi)
+ " " + Games.Players.getCurrentPlayer(getApiClient()).getDisplayName() + "!");
if(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_PLAYED) != 0) {
((TextView) findViewById(R.id.playerDispScoreId)).setText(
getString(R.string.str_gms_disp_rating) + mSaveGame.getStatsFromName(ConstantsData.CH_KEY_RATIND) +
" (" + getString(R.string.str_gms_disp_win) + mSaveGame.getStatsFromName(ConstantsData.CH_KEY_WON) +
", " + getString(R.string.str_gms_disp_draw) + mSaveGame.getStatsFromName(ConstantsData.CH_KEY_DRAW) +
", " + getString(R.string.str_gms_disp_loss) + mSaveGame.getStatsFromName(ConstantsData.CH_KEY_LOST) + ")");
} else {
((TextView) findViewById(R.id.playerDispScoreId)).setVisibility(View.GONE);
}
}
void startQuickGame(int variant) {
// quick-start a game with 1 randomly selected opponent
final int MIN_OPPONENTS = 1, MAX_OPPONENTS = 1;
Bundle autoMatchCriteria = RoomConfig.createAutoMatchCriteria(MIN_OPPONENTS,
MAX_OPPONENTS, 0);
RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
rtmConfigBuilder.setMessageReceivedListener(this);
rtmConfigBuilder.setRoomStatusUpdateListener(this);
rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
rtmConfigBuilder.setVariant(variant);
switchToScreen(R.id.screen_wait);
keepScreenOn();
//resetGameVars();
Games.RealTimeMultiplayer.create(getApiClient(), rtmConfigBuilder.build());
}
// Handle the result of the "Select players UI" we launched when the user clicked the
// "Invite friends" button. We react by creating a room with those players.
private void handleSelectPlayersResult(int response, Intent data, int variant) {
if (response != Activity.RESULT_OK) {
//Log.w(TAG, "*** select players UI cancelled, " + response);
switchToMainScreen();
return;
}
Log.d(TAG, "Select players UI succeeded.");
// get the invitee list
final ArrayList<String> invitees = data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
//Log.d(TAG, "Invitee count: " + invitees.size());
// get the automatch criteria
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers = data.getIntExtra(Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers = data.getIntExtra(Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0 || maxAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
//Log.d(TAG, "Automatch criteria: " + autoMatchCriteria);
}
// create the room
Log.d(TAG, "Creating room...");
RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
rtmConfigBuilder.addPlayersToInvite(invitees);
rtmConfigBuilder.setMessageReceivedListener(this);
rtmConfigBuilder.setRoomStatusUpdateListener(this);
if (autoMatchCriteria != null) {
rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
}
rtmConfigBuilder.setVariant(variant);
switchToScreen(R.id.screen_wait);
keepScreenOn();
resetGameVars();
resetGameBoolVars();
Games.RealTimeMultiplayer.create(getApiClient(), rtmConfigBuilder.build());
Log.d(TAG, "Room created, waiting for it to be ready...");
}
// Handle the result of the invitation inbox UI, where the player can pick an invitation
// to accept. We react by accepting the selected invitation, if any.
private void handleInvitationInboxResult(int response, Intent data) {
if (response != Activity.RESULT_OK) {
Log.w(TAG, "*** invitation inbox UI cancelled, " + response);
switchToMainScreen();
return;
}
Log.d(TAG, "Invitation inbox UI succeeded.");
Invitation inv = data.getExtras().getParcelable(Multiplayer.EXTRA_INVITATION);
// accept invitation
acceptInviteToRoom(inv.getInvitationId());
}
// Accept the given invitation.
void acceptInviteToRoom(String invId) {
// accept the invitation
Log.d(TAG, "Accepting invitation: " + invId);
RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(this);
roomConfigBuilder.setInvitationIdToAccept(invId)
.setMessageReceivedListener(this)
.setRoomStatusUpdateListener(this);
switchToScreen(R.id.screen_wait);
keepScreenOn();
resetGameVars();
resetGameBoolVars();
Games.RealTimeMultiplayer.join(getApiClient(), roomConfigBuilder.build());
}
// Handle back key to make sure we cleanly leave a game if we are in the middle of one
@Override
public boolean onKeyDown(int keyCode, KeyEvent e) {
if (keyCode == KeyEvent.KEYCODE_BACK && mCurScreen == R.id.screen_game) {
if(!isLeaveRoom && isMatch)
showDialog(GAME_GMS_EXIT);
return true;
}
return super.onKeyDown(keyCode, e);
}
// Leave the room.
void leaveRoom() {
isLeaveRoom = true;
stopKeepingScreenOn();
if (mRoomId != null) {
Games.RealTimeMultiplayer.leave(getApiClient(), this, mRoomId);
mRoomId = null;
switchToScreen(R.id.screen_wait);
} else {
switchToMainScreen();
}
}
// Show the waiting room UI to track the progress of other players as they enter the
// room and get connected.
void showWaitingRoom(Room room) {
// minimum number of players required for our game
// For simplicity, we require everyone to join the game before we start it
// (this is signaled by Integer.MAX_VALUE).
final int MIN_PLAYERS = Integer.MAX_VALUE;
Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(getApiClient(), room, MIN_PLAYERS);
// show waiting room UI
startActivityForResult(i, RC_WAITING_ROOM);
}
@Override
public void onInvitationReceived(Invitation invitation) {
// We got an invitation to play a game! So, store it in
// mIncomingInvitationId
// and show the popup on the screen.
mIncomingInvitationId = invitation.getInvitationId();
((TextView) findViewById(R.id.incoming_invitation_text)).setText(
invitation.getInviter().getDisplayName() + " " +
getString(R.string.is_inviting_you));
switchToScreen(mCurScreen); // This will show the invitation popup
}
@Override
public void onInvitationRemoved(String arg0) {
// TODO Auto-generated method stub
}
@Override
public void onJoinedRoom(int statusCode, Room room) {
Log.d(TAG, "onJoinedRoom(" + statusCode + ", " + room + ")");
if (statusCode != GamesStatusCodes.STATUS_OK) {
Log.e(TAG, "*** Error: onRoomConnected, status " + statusCode);
showGameError();
return;
}
// show the waiting room UI
showWaitingRoom(room);
}
// Called when we've successfully left the room (this happens a result of voluntarily leaving
// via a call to leaveRoom(). If we get disconnected, we get onDisconnectedFromRoom()).
@Override
public void onLeftRoom(int statusCode, String roomId) {
// we have left the room; return to main screen.
Log.d(TAG, "onLeftRoom, code " + statusCode);
switchToMainScreen();
}
// Called when room is fully connected.
@Override
public void onRoomConnected(int statusCode, Room room) {
Log.d(TAG, "onRoomConnected(" + statusCode + ", " + room + ")");
if (statusCode != GamesStatusCodes.STATUS_OK) {
Log.e(TAG, "*** Error: onRoomConnected, status " + statusCode);
showGameError();
return;
}
updateRoom(room);
}
// Called when room has been created
@Override
public void onRoomCreated(int statusCode, Room room) {
Log.d(TAG, "onRoomCreated(" + statusCode + ", " + room + ")");
if (statusCode != GamesStatusCodes.STATUS_OK) {
Log.e(TAG, "*** Error: onRoomCreated, status " + statusCode);
showGameError();
return;
}
// show the waiting room UI
showWaitingRoom(room);
}
@Override
public void onConnectedToRoom(Room room) {
Log.d(TAG, "onConnectedToRoom.");
// get room ID, participants and my ID:
mRoomId = room.getRoomId();
mParticipants = room.getParticipants();
mMyId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
// print out the list of participants (for debug purposes)
//Log.d(TAG, "Room ID: " + mRoomId);
//Log.d(TAG, "My ID " + mMyId);
//Log.d(TAG, "<< CONNECTED TO ROOM>>");
gmsGameVariantNumber = room.getVariant();
if(isServer()) {
imFirstType = -1;
sendFirstTypeForStart();
}
sendMyStats();
}
// Called when we get disconnected from the room. We return to the main screen.
@Override
public void onDisconnectedFromRoom(Room room) {
mRoomId = null;
if(opponentLeave) {
Toast.makeText(this, getString(R.string.str_gms_leave_opponent_toast), Toast.LENGTH_SHORT).show();
leaveRoom();
} else
showGameError();
}
// Show error message about game being cancelled and return to main screen.
void showGameError() {
//showAlert(getString(R.string.error), getString(R.string.game_problem));
switchToMainScreen();
}
void updateRoom(Room room) {
if (room != null) {
mParticipants = room.getParticipants();
}
}
/*
* GAME LOGIC
*/
// Reset game variables in preparation for a new game.
void resetGameVars() {
mParticipantScore.clear();
mFinishedParticipants.clear();
}
void resetGameBoolVars() {
imReady = false;
opponentReady = false;
isOpponentResign = false;
isOpponentTimeOut = false;
}
// Current state of the game:
int mSecondsLeft = -1; // how long until the game ends (seconds)
int GAME_DURATION = -1;
// Start the gameplay phase of the game.
void startGame(boolean multiplayer, int variant) {
resetGameVars();
switch (variant) {
case ConstantsData.GAME_VARIANT_LONG:
mSecondsLeft = -1;
GAME_DURATION = -1;
((TextView) findViewById(R.id.countdown)).setText("0:00");
break;
case ConstantsData.GAME_VARIANT_1MIN:
mSecondsLeft = ConstantsData.GAME_DURATION_1MIN;
GAME_DURATION = ConstantsData.GAME_DURATION_1MIN;
break;
case ConstantsData.GAME_VARIANT_2MIN:
mSecondsLeft = ConstantsData.GAME_DURATION_2MIN;
GAME_DURATION = ConstantsData.GAME_DURATION_2MIN;
break;
case ConstantsData.GAME_VARIANT_3MIN:
mSecondsLeft = ConstantsData.GAME_DURATION_3MIN;
GAME_DURATION = ConstantsData.GAME_DURATION_3MIN;
break;
default:
break;
}
isLeaveRoom = false;
mMultiplayer = multiplayer;
switchToScreen(R.id.screen_game);
startMultiplayerGameMode(imFirstType);
if(mSecondsLeft > 0) {
mHandler = new Handler();
myRunnable = new Runnable() {
@Override
public void run() {
if (mSecondsLeft <= 0)
return;
gameTick();
mHandler.postDelayed(this, 1000);
}
};
// run the gameTick() method every second to update the game.
mHandler.postDelayed(myRunnable, 1000);
}
resetGameBoolVars();
}
// Game tick -- update countdown, check if game ended.
void gameTick() {
if (mSecondsLeft > 0) {
if(myTurn)
--mSecondsLeft;
else
mSecondsLeft = GAME_DURATION;
if(mSecondsLeft <= 10)
((TextView) findViewById(R.id.countdown)).setTextColor(Color.RED);
else
((TextView) findViewById(R.id.countdown)).setTextColor(Color.WHITE);
}
// update countdown
((TextView) findViewById(R.id.countdown)).setText(getCountDownText(mSecondsLeft));
if (mSecondsLeft <= 0) {
// finish game
timeOutGMSGame();
}
}
String getCountDownText(int shownTime) {
String result = null;
if (shownTime == 180)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_180;
else if (shownTime < 180 && shownTime >= 130)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_160 + Integer.toString(shownTime - 120);
else if (shownTime < 130 && shownTime >= 120)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_130 + Integer.toString(shownTime - 120);
else if (shownTime == 120)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_120;
else if(shownTime < 120 && shownTime >= 70)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_100 + Integer.toString(shownTime - 60);
else if(shownTime < 70 && shownTime >= 60)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_70 + Integer.toString(shownTime - 60);
else if(shownTime < 60 && shownTime >= 10)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_60 + Integer.toString(shownTime);
else if(shownTime < 10)
result = ConstantsData.COUNTDOWN_TIMER_TEXT_9 + Integer.toString(shownTime);
return result;
}
/*
* COMMUNICATIONS SECTION. Methods that implement the game's network
* protocol.
*/
// Score of other participants. We update this as we receive their scores
// from the network.
Map<String, Integer> mParticipantScore = new HashMap<String, Integer>();
// Participants who sent us their final score.
Set<String> mFinishedParticipants = new HashSet<String>();
// Called when we receive a real-time message from the network.
// Messages in our game are made up of 2 bytes: the first one is 'F' or 'U'
// indicating
// whether it's a final or interim score. The second byte is the score.
// There is also the
// 'S' message, which indicates that the game should start.
@Override
public void onRealTimeMessageReceived(RealTimeMessage rtm) {
byte[] buf = rtm.getMessageData();
String sender = rtm.getSenderParticipantId();
//Log.d(TAG, "Message received: " + (char) buf[0] + "/" + (int) buf[1]);
if (buf[0] == 'S') {
if ((int) buf[1] == 1)
imFirstType = 0;
else
imFirstType = 1;
}
if (buf[0] == 'F') {
if(imReady) {
startGame(true, gmsGameVariantNumber);
} else {
opponentReady = true;
}
}
if (buf[0] == 'M') {
handleGMSClick((int) buf[1], (int) buf[2]);
}
if (buf[0] == 'D') {
showDialog(GAME_GMS_DRAW_ASK);
}
if (buf[0] == '=') {
ctrl.acceptDrawGmsGame();
Toast.makeText(this, getString(R.string.draw_agree), Toast.LENGTH_SHORT).show();
}
if (buf[0] == '!') {
Toast.makeText(this, "Not draw!", Toast.LENGTH_SHORT).show();
}
if (buf[0] == 'R') {
isOpponentResign = true;
ctrl.resignGame();
if(imFirstType == 0)
Toast.makeText(this, getString(R.string.resign_black), Toast.LENGTH_SHORT).show();
else
Toast.makeText(this, getString(R.string.resign_white), Toast.LENGTH_SHORT).show();
}
if (buf[0] == 'T') {
isOpponentTimeOut = true;
ctrl.resignGame();
if(imFirstType == 0)
Toast.makeText(this, getString(R.string.gms_white_win_time), Toast.LENGTH_SHORT).show();
else
Toast.makeText(this, getString(R.string.gms_black_win_time), Toast.LENGTH_SHORT).show();
}
if(buf.length > 10) {
if (buf[2] == 's' && buf[3] == 't' && buf[4] == 'a' && buf[5] == 't') {
try {
//sendMyStats();
loadOppStatsFromJson(new String(buf));
} catch (Exception e) {
Toast.makeText(this, "Error load opponent stats", Toast.LENGTH_SHORT).show();
}
}
}
}
/**
* Broadcast move square.
*
* @param sq move selected square to this position/square (to)
*
* @param selSquare selected square (from)
*
*/
void broadcastMove(int sq, int selSquare) {
myTurn = false;
// Message buffer for sending messages
byte[] mMsgBuf = new byte[3];
if (!mMultiplayer)
return; // playing single-player mode
// First byte in message indicates whether it's a final score or not
mMsgBuf[0] = (byte)'M';
mMsgBuf[1] = (byte)sq;
mMsgBuf[2] = (byte)selSquare;
// Send to every other participant.
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mMsgBuf,
mRoomId, p.getParticipantId());
}
}
private final void handleGMSClick(int sq, int selSquare) {
myTurn = true;
Move m = cb.mousePressedGMS2(sq, selSquare);
if(m != null) {
ctrl.makeHumanMoveGMS(m, true);
}
setEgtbHints(cb.getSelectedSquare());
}
/**
* if the current player is server, it generates a random number (0=WHITE or 1=BLACK) and sends an opponent
*/
void sendFirstTypeForStart() {
if(imFirstType == -1) {
byte[] mStartMsg = new byte[2];
mStartMsg[0] = (byte) 'S';
imFirstType = new Random().nextInt(2);
mStartMsg[1] = (byte) imFirstType;
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
}
void sendImReady() {
byte[] mStartMsg = new byte[1];
mStartMsg[0] = (byte) 'F';
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
void sendAskDrawIsPossible() {
byte[] mStartMsg = new byte[1];
mStartMsg[0] = (byte) 'D';
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
void sendAnswerDrawIsPossible(boolean answer) {
byte[] mStartMsg = new byte[1];
if (answer)
mStartMsg[0] = (byte) '=';
else
mStartMsg[0] = (byte) '!';
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
void sendResignGame() {
byte[] mStartMsg = new byte[1];
mStartMsg[0] = (byte) 'R';
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
/**
* Sending network packet if the current player ran out of time (GMS mode).
*/
void sendTimeOutGame() {
byte[] mStartMsg = new byte[1];
mStartMsg[0] = (byte) 'T';
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
/**
* Send current player stats to other player (GMS mode).
*/
void sendMyStats() {
mSaveGame.setUserName(Games.Players.getCurrentPlayer(getApiClient()).getDisplayName());
byte[] mStartMsg = mSaveGame.toBytes();
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId))
continue;
if (p.getStatus() != Participant.STATUS_JOINED)
continue;
Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), this, mStartMsg,
mRoomId, p.getParticipantId());
}
}
/*
* UI SECTION. Methods that implement the game's UI.
*/
// This array lists all the individual screens our game has.
final static int[] CLICKABLES = {
R.id.button_accept_popup_invitation, R.id.button_invite_players,
R.id.button_quick_game, R.id.button_see_invitations, R.id.button_sign_in,
R.id.button_sign_out, R.id.button_leaderboard, R.id.button_achive
};
final static int[] SCREENS = {
R.id.screen_game, R.id.screen_main, R.id.screen_sign_in,
R.id.screen_wait
};
int mCurScreen = -1;
void switchToScreen(int screenId) {
// make the requested screen visible; hide all others.
for (int id : SCREENS) {
findViewById(id).setVisibility(screenId == id ? View.VISIBLE : View.GONE);
}
mCurScreen = screenId;
// should we show the invitation popup?
boolean showInvPopup;
if (mIncomingInvitationId == null) {
// no invitation, so no popup
showInvPopup = false;
} else if (mMultiplayer) {
// if in multiplayer, only show invitation on main screen
showInvPopup = (mCurScreen == R.id.screen_main);
} else {
// single-player: show on main screen and gameplay screen
showInvPopup = (mCurScreen == R.id.screen_main || mCurScreen == R.id.screen_game);
}
findViewById(R.id.invitation_popup).setVisibility(showInvPopup ? View.VISIBLE : View.GONE);
}
void switchToMainScreen() {
switchToScreen(isSignedIn() ? R.id.screen_main : R.id.screen_sign_in);
}
void showAdsGMS() {
// Begin loading your
interstitial = new InterstitialAd(this);
interstitial.setAdUnitId(ConstantsData.INTERSTITIAL_APPID);
//adRequest = new AdRequest.Builder().build();
//interstitial.loadAd(adRequest);
// Set Ad Listener to use the callbacks below
//interstitialsetAdListener(this);
// End AdMob
// AdMob
//if (synth.getId() % 2 == 0) {
// if(interstitial.isLoaded())
// interstitial.show();
//}
}
// Sets the flag to keep this screen on. It's recommended to do that during
// the
// handshake when setting up a game, because if the screen turns off, the
// game will be
// cancelled.
void keepScreenOn() {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
// Clears the flag that keeps the screen on.
void stopKeepingScreenOn() {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
public void onStart() {
if(!isSinglePlayer) {
switchToScreen(R.id.screen_wait);
mClient.connect();
}
super.onStart();
}
@Override
public void onStop() {
if(!isSinglePlayer) {
leaveRoom();
switchToScreen(R.id.screen_wait);
}
super.onStop();
}
@Override
public void onRealTimeMessageSent(int statusCode, int tokenId,
String recipientParticipantId) {
// TODO Auto-generated method stub
}
private boolean isServer()
{
for(Participant p : mParticipants )
{
String pid = p.getParticipantId();
if (pid.equals(mMyId)) {
continue;
}
if(pid.compareTo(mMyId)<0)
return false;
}
return true;
}
private void storeScoreToLeaderBoard(long score) {
Games.Leaderboards.submitScore(getApiClient(), getString(R.string.leaderboard_score),
score);
}
private void storeWinnersToLeaderBoard(long win) {
Games.Leaderboards.submitScore(getApiClient(), getString(R.string.leaderboard_winners),
win);
}
final static int[] ACHIEVEMENT = {
R.string.achievement_1200, R.string.achievement_1400, R.string.achievement_1600,
R.string.achievement_1800, R.string.achievement_2000,R.string.achievement_2200,
R.string.achievement_2400, R.string.achievement_2500,R.string.achievement_2500_GM,
R.string.achievement_2600_PCM_1, R.string.achievement_2700_PCM_2, R.string.achievement_2800_CM
};
void unlockAchievement(int rating) {
int index = ConstantsData.getIndexFromCheckedRange(rating);
Games.Achievements.unlock(getApiClient(), getString(ACHIEVEMENT[index]));
}
private final Dialog gameGmsModeDialog() {
final CharSequence[] items = {
getString(R.string.option_resign_game),
getString(R.string.option_draw),
getString(R.string.load_save_menu)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.select_game_mode);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
int gameModeType = -1;
/* only flip site in case the player was specified resp. changed */
boolean flipSite = false;
switch (item) {
case 0:
resighGMSGame();
break;
case 1:
if(!isLeaveRoom) {
if (ctrl.humansTurn()) {
if (ctrl.claimDrawIfPossible()) {
ctrl.stopPonder();
} else {
Toast.makeText(getApplicationContext(), R.string.offer_draw, Toast.LENGTH_SHORT).show();
}
}
sendAskDrawIsPossible();
}
break;
case 2:
removeDialog(FILE_MENU_DIALOG);
showDialog(FILE_MENU_DIALOG);
break;
default: break;
}
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
return alert;
}
private void resighGMSGame() {
if(!isLeaveRoom) {
if (ctrl.humansTurn()) {
ctrl.resignGame();
sendResignGame();
}
}
}
private void timeOutGMSGame() {
if(!isLeaveRoom) {
if (ctrl.humansTurn()) {
ctrl.resignGame();
sendTimeOutGame();
}
}
}
private final Dialog newGmsGameDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.option_new_game);
builder.setMessage(R.string.start_new_game);
builder.setPositiveButton(R.string.yes, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(imFirstType == 0)
imFirstType = 1;
else
imFirstType = 0;
if (!opponentReady && !isLeaveRoom) {
imReady = true;
sendImReady();
Toast.makeText(getApplicationContext(), getString(R.string.draw_gms_try_again), Toast.LENGTH_SHORT).show();
} else {
if(!isLeaveRoom) {
sendImReady();
startGame(true, gmsGameVariantNumber);
}
}
}
});
builder.setNeutralButton(R.string.no, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
leaveRoom();
}
});
return builder.create();
}
private final Dialog drawGmsGameDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.draw_gms_option_title);
builder.setMessage(R.string.draw_gms_option_msg);
builder.setPositiveButton(R.string.yes, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(!isLeaveRoom) {
sendAnswerDrawIsPossible(true);
ctrl.acceptDrawGmsGame();
}
}
});
builder.setNeutralButton(R.string.no, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(!isLeaveRoom)
sendAnswerDrawIsPossible(false);
}
});
return builder.create();
}
private final Dialog gameGmsAutoMatchVariantDialog() {
final CharSequence[] items = {
getString(R.string.gms_variant_opt_long_game),
getString(R.string.gms_variant_opt_1min_game),
getString(R.string.gms_variant_opt_2min_game),
getString(R.string.gms_variant_opt_3min_game)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.gms_variant_opt_title);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
int gameModeType = -1;
/* only flip site in case the player was specified resp. changed */
boolean flipSite = false;
switch (item) {
case 0:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_LONG;
startQuickGame(ConstantsData.GAME_VARIANT_LONG);
break;
case 1:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_1MIN;
startQuickGame(ConstantsData.GAME_VARIANT_1MIN);
break;
case 2:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_2MIN;
startQuickGame(ConstantsData.GAME_VARIANT_2MIN);
break;
case 3:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_3MIN;
startQuickGame(ConstantsData.GAME_VARIANT_3MIN);
break;
default: break;
}
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
return alert;
}
private final Dialog exitGmsGameDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.gms_exit_game_dialog_title);
builder.setMessage(R.string.gms_exit_game_dialog_msg);
builder.setPositiveButton(R.string.yes, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
resighGMSGame();
leaveRoom();
}
});
builder.setNeutralButton(R.string.no, new Dialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
return builder.create();
}
private final Dialog gameGmsInviteVariantDialog() {
final CharSequence[] items = {
getString(R.string.gms_variant_opt_long_game),
getString(R.string.gms_variant_opt_1min_game),
getString(R.string.gms_variant_opt_2min_game),
getString(R.string.gms_variant_opt_3min_game)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.gms_variant_opt_title);
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
int gameModeType = -1;
/* only flip site in case the player was specified resp. changed */
boolean flipSite = false;
switch (item) {
case 0:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_LONG;
invateGMSPlayers();
break;
case 1:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_1MIN;
invateGMSPlayers();
break;
case 2:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_2MIN;
invateGMSPlayers();
break;
case 3:
gmsGameVariantNumber = ConstantsData.GAME_VARIANT_3MIN;
invateGMSPlayers();
break;
default: break;
}
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
return alert;
}
private void invateGMSPlayers() {
// show list of invitable players
Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(getApiClient(), 1, 1);
switchToScreen(R.id.screen_wait);
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
/**
* Cloud Save section
*/
private void loadLocal() {
SharedPreferences sp = getSharedPreferences("gameStateGMS", Context.MODE_PRIVATE);
mSaveGame = new SaveGame(sp, "gameStateGMS");
String json = sp.getString("gameStateGMS", "");
if (json == null || json.trim().equals("")) mAlreadyLocalState = false;
else mAlreadyLocalState = true;
}
private void saveLocal() {
SharedPreferences sp = getSharedPreferences("gameStateGMS", Context.MODE_PRIVATE);
if(getApiClient().isConnected())
mSaveGame.setUserName(Games.Players.getCurrentPlayer(getApiClient()).getDisplayName());
if(mClient.isConnected())
mSaveGame.setUserName(Games.Players.getCurrentPlayer(getApiClient()).getDisplayName());
mSaveGame.save(sp, "gameStateGMS");
}
ResultCallback<AppStateManager.StateResult> mResultCallback = new
ResultCallback<AppStateManager.StateResult>() {
@Override
public void onResult(AppStateManager.StateResult result) {
AppStateManager.StateConflictResult conflictResult = result.getConflictResult();
AppStateManager.StateLoadedResult loadedResult = result.getLoadedResult();
if (loadedResult != null) {
processStateLoaded(loadedResult);
} else if (conflictResult != null) {
processStateConflict(conflictResult);
}
}
};
void loadFromCloud() {
AppStateManager.load(mClient, OUR_STATE_KEY).setResultCallback(mResultCallback);
}
void saveToCloud() {
if (mClient.isConnected()) {
mSaveGame.setUserName(Games.Players.getCurrentPlayer(getApiClient()).getDisplayName());
AppStateManager.update(mClient, OUR_STATE_KEY, mSaveGame.toBytes());
//Toast.makeText(getApplicationContext(), "���������� � ������ ����� ������� 1: " + new String(mSaveGame.toBytes()), Toast.LENGTH_SHORT).show();
} else {
}
// Note: this is a fire-and-forget call. It will NOT trigger a call to any callbacks!
}
private void processStateConflict(AppStateManager.StateConflictResult result) {
// Need to resolve conflict between the two states.
// We do that by taking the union of the two sets of cleared levels,
// which means preserving the maximum star rating of each cleared
// level:
byte[] serverData = result.getServerData();
byte[] localData = result.getLocalData();
SaveGame localGame = new SaveGame(localData);
SaveGame serverGame = new SaveGame(serverData);
SaveGame resolvedGame = localGame.unionWith(serverGame);
AppStateManager.resolve(mClient, result.getStateKey(), result.getResolvedVersion(),
resolvedGame.toBytes()).setResultCallback(mResultCallback);
}
private void processStateLoaded(AppStateManager.StateLoadedResult result) {
switch (result.getStatus().getStatusCode()) {
case AppStateStatusCodes.STATUS_OK:
// Data was successfully loaded from the cloud: merge with local data.
mSaveGame = mSaveGame.unionWith(new SaveGame(result.getLocalData()));
mAlreadyLoadedState = true;
saveLocal();
//Toast.makeText(getApplicationContext(), "�������� �� ������ �������: " + new String(result.getLocalData()), Toast.LENGTH_SHORT).show();
//hideAlertBar();
break;
case AppStateStatusCodes.STATUS_STATE_KEY_NOT_FOUND:
// key not found means there is no saved data. To us, this is the same as
// having empty data, so we treat this as a success.
mAlreadyLoadedState = true;
//hideAlertBar();
break;
case AppStateStatusCodes.STATUS_NETWORK_ERROR_NO_DATA:
// can't reach cloud, and we have no local state. Warn user that
// they may not see their existing progress, but any new progress won't be lost.
//showAlertBar(R.string.no_data_warning);
break;
case AppStateStatusCodes.STATUS_NETWORK_ERROR_STALE_DATA:
// can't reach cloud, but we have locally cached data.
//showAlertBar(R.string.stale_data_warning);
break;
case AppStateStatusCodes.STATUS_CLIENT_RECONNECT_REQUIRED:
// need to reconnect AppStateClient
reconnectClient();
break;
default:
// error
//showAlertBar(R.string.load_error_warning);
break;
}
updateRatingUi();
}
private void showAlertBar(int resId) {
// ((TextView) findViewById(R.id.alert_bar)).setText(getString(resId));
// ((TextView) findViewById(R.id.alert_bar)).setVisibility(View.VISIBLE);
}
private void hideAlertBar() {
//((TextView) findViewById(R.id.alert_bar)).setVisibility(View.GONE);
}
/**
* refresh Rating in game after match
*/
private void updateRatingUi() {
//todo
}
private void handleGMSMatchComplete(int result) {
if(mHandler != null)
mHandler.removeCallbacks(myRunnable);
isMatch = false;
if(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_PLAYED) == 0) {
unlockAchievement(0);
loadFromCloud();
}
mSaveGame.setStatsFromResult(result, opponentRating, gameTypeMode);
saveLocal();
saveToCloud();
sendMyStats();
storeScoreToLeaderBoard(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_RATIND));
storeWinnersToLeaderBoard(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_WON));
unlockAchievement(mSaveGame.getStatsFromName(ConstantsData.CH_KEY_RATIND));
showDialog(NEW_GMS_GAME_DIALOG);
}
public void loadOppStatsFromJson(String json) {
//mOpponentStats.clear();
if (json == null || json.trim().equals("")) return;
try {
JSONObject obj = new JSONObject(json);
String format = obj.getString("version");
if (!format.equals(ConstantsData.SERIAL_VERSION)) {
throw new RuntimeException("Unexpected loot format " + format);
}
opponentName = obj.getString("username");
JSONObject stats = obj.getJSONObject("stats");
Iterator<?> iter = stats.keys();
while (iter.hasNext()) {
String statName = (String)iter.next();
mOpponentStats.put(statName, stats.getInt(statName));
}
Integer r = 0;
r = mOpponentStats.get("rating");
opponentRating = r.intValue();
}
catch (JSONException ex) {
ex.printStackTrace();
throw new RuntimeException("Opponent stats data has a syntax error: " + json, ex);
}
catch (NumberFormatException ex) {
ex.printStackTrace();
throw new RuntimeException("Opponent stats has an invalid number in it: " + json, ex);
}
}
}